JavaScript五種原型鏈
在了解JavaScript的原型鏈前,先來看看以下代碼:
function Animal() { } Animal.prototype.species = "動物"; function Cat(name) { Animal.call(this); this.name = name; } Cat.prototype = new Animal(); var cat1 = new Cat("大毛"); console.log(cat1.species);
這個示例代碼中,定義了一個Animal類和一個Cat類,Cat類繼承自Animal類。最后輸出cat1的species屬性,我們可以得到輸出結果“動物”。
那么為什么cat1可以訪問到species屬性呢?這就是因為JavaScript的原型鏈特性。
一、構造函數與原型對象
在JavaScript中,每個對象都有一個內部屬性[[Prototype]],可以通過__proto__(非標準屬性)獲得。通俗來講,一個實例對象的__proto__指向它所繼承的構造函數的prototype。
//創建一個基本的構造函數 function A() { } var a = new A(); console.log(a.__proto__ === A.prototype); // true
通過這種方式實例a可以訪問到A.prototype中定義的屬性和方法。
二、原型鏈
如果在一個對象中無法找到需要訪問的屬性或方法,它就會搜索它自己的[[Prototype]],也就是它繼承的構造函數的prototype,而這個對象的[[Prototype]]會指向它父親的[[Prototype]],一直到達原型鏈的頂層——Object.prototype。
在上面的例子中,Cat.prototype.__proto__ 指向 Animal.prototype,Animal.prototype.__proto__ 指向 Object.prototype,Object.prototype.__proto__ 指向 null。
var obj = new Object(); console.log(obj.__proto__ === Object.prototype); // true function Foo() { } console.log(Foo.__proto__ === Function.prototype); // true console.log(Foo.prototype.__proto__ === Object.prototype); // true
三、函數和對象的區別
JavaScript中的函數也是一個對象。對象的__proto__指向Object.prototype,而函數的__proto__指向Function.prototype。因此,又可以看出函數被視為對象的一種特殊形式。
function Foo() {} console.log(Foo.__proto__ === Function.prototype); // true console.log(Foo.prototype.__proto__ === Object.prototype); // true
四、函數的原型與實例的原型
JavaScript中的“繼承”。一個實例的原型為其構造函數的prototype,而一個構造函數的prototype的原型則為Object.prototype。
function Foo() { } Foo.prototype.setName = function() {console.log('Foo');}; var foo = new Foo(); console.log(foo.__proto__ === Foo.prototype); // true console.log(Foo.prototype.__proto__ === Object.prototype);// true
五、constructor的作用
每個對象都有一個constructor屬性,指向創建該對象的構造函數。通過這個屬性可以判斷一個實例對象的類型。
function Foo() {} var foo = new Foo(); console.log(foo.constructor === Foo); // true console.log(Foo.constructor === Function); // true
總結
通過JavaScript的原型鏈,我們可以實現代碼的復用,避免重復的冗余代碼。實現JavaScript的面向對象編程。