原型鏈算是js中最基礎的問題了,可是時間久了我有時候會混淆,爲了下次可以快速回憶起來,特寫此文章增強記憶!markdown
function Person() {
//我是一個構造函數
}
var person = new Person();
person.name = 'Kevin';
console.log(person.name) // Kevin
複製代碼
至此,咱們已經建立了一個簡單的構造函數 ---Person函數
同時咱們使用 new 建立了一個實例對象 ---personui
很簡單吧,接下來進入正題!spa
首先明確一點:每一個函數都有prototype屬性,而且prototype屬性是函數纔會有的屬性prototype
function Person() {
}
Person.prototype.name = 'Kevin';
var person1 = new Person();
var person2 = new Person();
console.log(person1.name) // Kevin
console.log(person2.name) // Kevin
複製代碼
思考一下構造函數Person的prototype指向什麼呢?3d
其實prototype指向的是一個對象,這個對象就是調用構造函數Person的實例的原型。code
換言之,構造函數Person的prototype指向的是實例person一、person2的原型。orm
用一張圖表示構造函數和實例原型之間的關係:對象
在這張圖中咱們用Object.prototype表示實例的原型繼承
那麼咱們應該如何表示實例(person1)與實例原型(Person.prototype)之間的關係呢?這就要講到第二個屬性了
首先明確一點: js中的每一個對象(除null之外)都有__proto__屬性,該屬性指向對象的原型。以下:
function Person() {
}
var person = new Person();
console.log(person.__proto__ === Person.prototype); // true
複製代碼
因而咱們更新下關係圖:
從上圖中能夠看到構造函數和實例對象均可以指向實例原型,那麼是否存在一個屬性指向構造函數和實例對象呢?
指向實例對象的屬性是沒有的,由於一個構造函數能夠有不少實例。可是!原型指向構造函數的卻是有!這就要講到第三個屬性:
首先明確一點: 每一個原型都有一個constructor屬性指向對應的構造函數
function Person() {
}
console.log(Person === Person.prototype.constructor); // true
複製代碼
因此再更新下關係圖:
瞭解了構造函數、實例原型、和實例之間的關係,接下來咱們講講實例和原型的關係:
當讀取實例的屬性時,若是找不到,就會查找與對象關聯的原型中的屬性,若是還查不到,就去找原型的原型,一直找到最頂層爲止。
舉個例子
function Person() {
}
Person.prototype.name = 'Kevin';
var person = new Person();
person.name = 'Daisy';
console.log(person.name) // Daisy
delete person.name;
console.log(person.name) // Kevin
複製代碼
當咱們訪問實例person的name屬性時,順利的找到person.name就是Daisy。可是當咱們刪除person的name屬性時, person 對象中找不到 name 屬性,就會訪問person實例的原型__proto__,也就是Person.prototype,順利的找到了Kevin。
可是萬一尚未找到呢?原型的原型又是什麼呢?
原型也是一個對象,既然是對象,咱們就能夠用最原始的方式建立它,那就是:
var obj = new Object();
obj.name = 'Kevin'
console.log(obj.name) // Kevin
複製代碼
其實原型對象(__ proto __)就是經過 Object 構造函數生成的,結合以前所講,實例的 proto 指向構造函數的 prototype ,因此咱們再更新下關係圖:
那 Object.prototype 的原型呢?
console.log(Object.prototype.__proto__ === null) // true
複製代碼
Object.prototype.proto 的值爲 null 就表明 Object.prototype 沒有原型。
因此查找屬性的時候查到 Object.prototype 就能夠中止查找了。
最終關係圖爲:
圖中由相互關聯的原型組成的鏈狀結構就是原型鏈,也就是藍色的這條線。
function Person() {
}
var person = new Person();
console.log(person.constructor === Person); // true
複製代碼
當獲取 person.constructor 時,其實 person 中並無 constructor 屬性,當不能讀取到constructor 屬性時,會從 person 的原型也就是 Person.prototype 中讀取,正好原型中有該屬性,因此:
person.constructor === Person.prototype.constructor
複製代碼
最後是關於繼承,前面咱們講到「每個對象都會從原型‘繼承’屬性」,實際上,繼承是一個十分具備迷惑性的說法,引用《你不知道的JavaScript》中的話,就是:
繼承意味着複製操做,然而 JavaScript 默認並不會複製對象的屬性,相反,JavaScript 只是在兩個對象之間建立一個關聯,這樣,一個對象就能夠經過委託訪問另外一個對象的屬性和函數,因此與其叫繼承,委託的說法反而更準確些。