JS每個對象都有一個内部屬性[[prototype]],我們通常稱之為原型.原型的值可以是一個對象,也可以是null.如果它的值是一個對象,則這個對象也一定有自己的原型.這樣就形成了一條線性的鍊,我們稱之為原型鍊。
通路一個對象的原型可以使用ES5中的Object.getPrototypeOf方法,或者ES6中的__proto__屬性。
原型鍊的作用是用來實作繼承,比如我們建立一個數組,數組的方法就是從數組的原型上繼承而來的。
在JavaScript中,每個函數 都有一個prototype屬性,當一個函數被用作構造函數來建立執行個體時,這個函數的prototype屬性值會被作為原型指派給所有對象執行個體(也就是設定 執行個體的`__proto__`屬性),也就是說,所有執行個體的原型引用的是函數的prototype屬性。
在JavaScript的原型對象中,還包含一個”constructor”屬性,這個屬性對應建立所有指向該原型的執行個體的構造函數。
”Object.prototype”的值就是”Object {}”這個原型對象。反過來,當通路”Object.prototype”對象的”constructor”這個屬性的時候,就得到了Obejct函數。
另外,當通過”Object.prototype._proto_”擷取Object原型的原型的時候,将會得到”null”,也就是說”Object {}”原型對象就是原型鍊的終點了。
Function對象作為一個函數,就會有prototype屬性,該屬性将對應”function () {}”對象。
Function對象作為一個對象,就有__proto__屬性,該屬性對應”Function.prototype”,也就是說,”Function._proto_ === Function.prototype”。
幾點了解
1.Object.__proto__ === Function.prototype // true
Object是函數對象,是通過new Function()建立,是以Object.__proto__指向Function.prototype。
2.Function.__proto__ === Function.prototype // true
Function 也是對象函數,也是通過new Function()建立,是以Function.__proto__指向Function.prototype。
3.Function.prototype.__proto__ === Object.prototype //true
Function.prototype是個函數對象,理論上他的__proto__應該指向 Function.prototype,就是他自己,自己指向自己,沒有意義,指向Object.prototype,Object.prototype.__proto__ === null,保證原型鍊能夠正常結束。
周遊原型鍊
我們沒有辦法周遊到所有以某個對象為原型的對象,但我們可以向上周遊,擷取到一個對象所有的上層原型,這個原型鍊必定是線性的,盡頭是null。
function getPrototypeChain(object) {
var protoChain = [];
while (object = object.__proto__) {
protoChain.push(object);
}
protoChain.push(null);
return protoChain;
}
試驗一下,不同的環境實作不同,顯示形式也不同.下面是在chrome控制台中的顯示.
>getPrototypeChain(new String(""))
[String, Object, null] //依次是String.prototype,Object.prototype,null
>getPrototypeChain(function(){})
[function Empty() {}, Object, null] //依次是Function.prototype,Object.prototype,null
内置類型的對象的原型鍊并不長,下面試試宿主對象.
>getPrototypeChain(document.createElement("div"))
[HTMLDivElement, HTMLElement, Element, Node, Object, null]
構造超長原型鍊
可以看出來,我們平時使用的對象并沒有很長的原型鍊.但可以自己構造一個.
function Foo() {}
for (var i = 0; i < 100; i++) {
Foo.prototype["foo" + i] = i;
Foo.prototype = new Foo;
console.dir(getPrototypeChain(new Foo));