原文出自:https://www.pandashen.com
概述
在 JavaScript 中有種說法叫 “萬物皆對象”,就是說無論是構造函數創建的實例,構造函數本身、原型對象、數組、函數本質上都是對象,都擁有 __proto__
屬性,即隱式原型,所有函數都擁有 prototype
屬性,即顯式原型(僅限函數),原型對象(prototype
屬性指向的對象),在定義函數時就被創建。
原型鏈指向概述
在 JavaScript 中整個原型鏈及查找機制用下圖可以完整的表示出來:
原型鏈指向:
1、通過字面量和 new Object()
所創建的對象,他們是構造函數是 function Object()
的實例,Object
構造函數的 prototype
指向原型對象 Object.prototype
,Object.prototype
的 constructor
指向構造函數 Object
,而實例的 __proto__
也指向 Object.prototype
,Object.prototype
的 __proto__
指向 null
,所以 Object.prototype
也叫做頂級原型對象。
2、上圖中 new Foo()
創建的對象是構造函數 function Foo()
的實例,Foo
的 prototype
指向原型對象 Foo.prototype
,Foo.prototype
的 constructor
指向構造函數 Foo
,而實例的 __proto__
也指向 Foo.prototype
,並且 Foo.prototype
雖然是原型對象,但也是對象,所以是構造函數 Object
的實例,__proto__
指向頂級原型對象 Object.prototype
。
3、數組的構造函數是 function Array()
原型鏈的指向與其他除 Object
以外的構造函數相同,Array.prototype
的 __proto__
也指向頂級原型對象 Object.prototype
,每一個數組都是 Array
的實例,__proto__
都指向 Array.prototype
。
4、Object
、Array
、Foo
等構造函數的本質也是對象,他們的構造函數是 function Function()
,Function
的 prototype
指向 Function.prototype
,Function.prototype
的 constructor
指向 Function
,所有的構造函數的 __proto__
都指向 Function.prototype
,包括 Function
本身,也就是說構造函數 Function
是由自己構造的,Function.prototype
的 __proto__
同樣指向頂級原型對象 Object.prototype
。
prototype 原型對象
prototype
是函數的一個屬性,屬性的值指向了一個對象,所以,只有函數纔有 prototype
原型對象。
function Person(name, age) {
this.name = name;
this.age = age;
}
typeof Person.prototype; // object
Person.prototype.constructor; // Person {}
Person.prototype.job = "qianduan";
var p1 = new Person("panda", 18);
var p2 = new Person("shen", 20);
p1.constructor.prototype; // 實例對象查找構造函數原型對象的方法
一般會把對象共有的屬性和方法都放在構造函數的原型對象上。
實例、構造函數、原型對象的關係
構造函數的原型 prototype
屬性指向一個原型對象,實例也可以通過 __proto__
指向原型對象,但本質上實例和構造函數之間是沒有關係的。
function Person(name, age) {
this.name = name;
this.age = age;
}
var p = new Person("nihao", 16);
p.constructor = { name: "haha" };
p.name; // nihao
上面的代碼中改變了構造函數的值爲一個對象,對象中的屬性 name
並沒有影響實例的 name
屬性值。
實例屬性 __proto__
上面訪問實例 p
的原型,實際使用 p.constructor.prototype
去找原型對象,當構造函數的值改變後是找不到原型對象的,所以實例並不是通過 constructor.prototype
去查找原型對象的,而是通過每一個實例都有的 __proto__
屬性,這個屬性指向創建實例的構造函數原本的原型對象,這個屬性不是標準,在 IE 下不存在。
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.job = "qianduan";
var p = new Person("nihao", 16);
p.__proto__.job; // qianduan
當構造函數的 prototype
屬性值被改變之後,在之前創建的實例的 __proto__
屬性值的仍然引用原型對象,所以對構造函數改變前創建的實例是沒有影響的,會影響後面創建的實例。
原型鏈查找機制
實例對象在調用了一個屬性或方法時,如果對象本身沒有這個屬性或方法,會去自己的原型對象查找,也就是 __proto__
中查找,如果原型對象中沒有,去原型對象的原型對象查找,一般(原型鏈沒有被修改)情況下就是去 __proto__
的 __proto__
中查找,即頂級原型對象 Object.prototype
,如果實例對象本身有這個屬性,則直接輸出,不再向上查找,如果對象本身和原型對象具有同名屬性,則會屏蔽掉原型對象的屬性。
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.job = "qianduan";
var p = new Person("nihao", 16);
p.job; // qianduan
p.job = "houtai";
p.job; // houtai
p.__proto__.job; // qianduan
總結
原型鏈的指向及原型鏈的查找機制是 JavaScript 中非常重要的基礎知識,理解原型鏈是更深入瞭解繼承和麪向對象編程的必經之路。