js原型鏈

原型鏈算是js中最基礎的問題了,可是時間久了我有時候會混淆,爲了下次可以快速回憶起來,特寫此文章增強記憶!markdown

簡單瞭解一下構造函數

function Person() {
    //我是一個構造函數
}
var person = new Person();
person.name = 'Kevin';
console.log(person.name) // Kevin
複製代碼

至此,咱們已經建立了一個簡單的構造函數 ---Person函數

同時咱們使用 new 建立了一個實例對象 ---personui

很簡單吧,接下來進入正題!spa

prototype屬性

首先明確一點:每一個函數都有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

用一張圖表示構造函數和實例原型之間的關係:對象

image.png

在這張圖中咱們用Object.prototype表示實例的原型繼承

那麼咱們應該如何表示實例(person1)與實例原型(Person.prototype)之間的關係呢?這就要講到第二個屬性了

__ proto __

首先明確一點: js中的每一個對象(除null之外)都有__proto__屬性,該屬性指向對象的原型。以下:

function Person() {

}
var person = new Person();
console.log(person.__proto__ === Person.prototype); // true
複製代碼

因而咱們更新下關係圖:

image.png

從上圖中能夠看到構造函數和實例對象均可以指向實例原型,那麼是否存在一個屬性指向構造函數和實例對象呢?

constructor

指向實例對象的屬性是沒有的,由於一個構造函數能夠有不少實例。可是!原型指向構造函數的卻是有!這就要講到第三個屬性:

首先明確一點: 每一個原型都有一個constructor屬性指向對應的構造函數

function Person() {

}
console.log(Person === Person.prototype.constructor); // true
複製代碼

因此再更新下關係圖:

image.png

瞭解了構造函數、實例原型、和實例之間的關係,接下來咱們講講實例和原型的關係:

實例與原型

當讀取實例的屬性時,若是找不到,就會查找與對象關聯的原型中的屬性,若是還查不到,就去找原型的原型,一直找到最頂層爲止。

舉個例子

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 ,因此咱們再更新下關係圖:

image.png

原型鏈

那 Object.prototype 的原型呢?

console.log(Object.prototype.__proto__ === null) // true
複製代碼

Object.prototype.proto 的值爲 null 就表明 Object.prototype 沒有原型。

因此查找屬性的時候查到 Object.prototype 就能夠中止查找了。

最終關係圖爲:

image.png 圖中由相互關聯的原型組成的鏈狀結構就是原型鏈,也就是藍色的這條線。

補充

constructor

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 只是在兩個對象之間建立一個關聯,這樣,一個對象就能夠經過委託訪問另外一個對象的屬性和函數,因此與其叫繼承,委託的說法反而更準確些。

相關文章
相關標籤/搜索