原型鏈
再上一篇有簡單講過原型:JavaScript(5)--- 面向對象 + 原型
講原型鏈知識之前,先說幾個重要的結論。
1、原型鏈就是 對象的__proto__所連接的鏈狀結構
2、prototype 屬性是函數獨有的
3、__proto__ 屬性是對象獨有的,實例原型(Object.prototype)也是對象,所以也會有__proto__屬性
下面我們一步一步來講解原型鏈
一、prototype屬性
1、構造函數創建對象
我們先使用構造函數創建一個對象:
function Person() {
}
var p = new Person();
p.name = 'xiaoxiao';
console.log(p.name); //xiaoxiao
2、 prototype屬性
概念
它是 函數獨有的屬性,它從一個函數指向另一個對象,代表這個對象是這個函數的原型對象,這個對象也是當前函數所創建的實例的原型對象。
prototype設計之初就是為了實現繼承,讓構造函數創建的所有實例,都能夠共享這個原型屬性和方法。
有了prototype我們不需要為每一個實例創建重復的屬性方法,而是將屬性方法創建在構造函數的原型對象上(prototype)。那些不需要共享的才創建在構造函數中。
示例
function Person(){
}
Person.prototype.age=18; //原型屬性
var p1 = new Person();
var p2 = new Person();
console.log(p1.age);//18
console.log(p2.age);//18
思考
這個函數的 prototype 屬性到底指向的是什么呢?
其實,函數的 prototype 屬性指向了一個對象,這個對象正是調用該構造函數而創建的實例的原型 。
思考
那么什么是原型呢?
每一個 JavaScript 對象 (null 除外 ) 在創建的時候就會與之關聯另一個對象,這個對象就是我們所說的原型,每一個對象都會從原型 ” 繼承 ” 屬性。
這里p1和p2就是實例對象,而Person.prototype就是它們的原型對象。p1和p2可以去繼承原型對象的方法和屬性。
讓我們用一張圖表示構造函數和實例原型之間的關系:

二、__proto__屬性
概念
__proto__屬性是對象(包括函數,函數也是對象)獨有的。__proto__屬性是從一個對象指向另一個對象,該屬性指向的就是該對象的原型對象(也可以理解為父對象)。
示例
function Person(){
}
var p = new Person();
console.log(p.__proto__ === Person.prototype);//true
更新下關系圖

__proto__通常稱為隱式原型,prototype為顯示原型,那我們可以說一個對象的隱式原型指向了該對象的構造函數的顯示原型。那么我們在顯示原型上定義的屬性方法,
通過隱式原型傳遞給了構造函數的實例。這樣一來實例就能很容易的訪問到構造函數原型上的方法和屬性了。
三、constructor屬性
概念
constructor是對象才有的屬性,它是從一個對象指向一個函數的。指向的函數就是該對象的構造函數。
注意了 實例原型也是對象,所以也會有constructor屬性,我們來驗證一下
function Person(){
}
console.log(Person === Person.prototype.constructor);//true
console.log(person.constructor); // ƒ Person(){}
再更新下關系圖

總結 通過上面的演示說明,我們可以得出
function Person(){
}
var person = new Person();
console.log(person.__proto__ === Person.prototype);//true
console.log(Person === Person.prototype.constructor);//true
了解了構造函數、實例原型、和實例之間的關系,接下來我們講講實例和原型的關系
四、實例與原型
當讀取實例的屬性時,如果找不到,就會查找與對象關聯的原型中的屬性,如果還查不到,就去找原型的原型,一直找到最頂層為止。
示例
function Person(){
}
var person = new Person();
Person.prototype.name='張三';
person.name='李四';
console.log(person.name);//李四
delete person.name;
console.log(person.name);//張三
在這個例子中,我們設置了 person 的 name 屬性,所以我們可以讀取到為 ’李四’ ,當我們刪除了 person 的 name 屬性時,讀取 person.name ,從 person 中找不到
就會從 person 的原型中查找,name為 ’張三’ 。但是萬一Person.prototype原型中還沒有找到呢?那會到原型的原型去查找。也就是Object.prototype
示例
function Person(){
}
var person = new Person();
Person.prototype.name='李四';
person.name='張三';
console.log(person.name);//張三
delete person.name;
console.log(person.name);//李四
Object.prototype.name='obj';
delete Person.prototype.name;
console.log(person.name);//obj
所以原型對象是通過 Object 構造函數生成的,結合之前所講 , 實例的 _proto_ 指向構造函數的 prototype, 所
再更新下關系圖

五、原型鏈
那 Object.prototypey也是對象,那它也應該會有原型。只不過有是有,只是為null
, 所以查到 Object.prototype 就可以停止查找了 。
所以最后一張關系圖就是

總結
圖中由相互關聯的原型組成的鏈狀結構就是原型鏈,也就是紅色的這條線
。
參考
1、JavaScript深入之從原型到原型鏈 非常感謝
別人罵我胖,我會生氣,因為我心里承認了我胖。別人說我矮,我就會覺得好笑,因為我心里知道我不可能矮。這就是我們為什么會對別人的攻擊生氣。
攻我盾者,乃我內心之矛(3)。