原型&原型鏈深度解讀
基本概念
原型鏈
首先我們看上面的示意圖,我們可以看到每一個建構函式都會有一個prototype屬性(js引擎自動幫我們加上的),這個屬性會指向一個原型物件,這個建構函式透過new 會生成一個新的例項物件,這個例項擁有一個__prto__
屬性,而這個屬性也會指向原型物件。
function A () {} // 建構函式const a = new A(); // 例項A.prototye === a.__proro__ // true
也就是說通常情況下(沒有手動給A.prototye
重新賦值)建構函式的prototype
和例項的__proto__
指向同一地址。而原型物件會有constructor
屬性指向這個建構函式。同樣原型物件也是一個物件,這個物件也會有他的__proto__
屬性,這個屬性又會指向另一個原型物件,這樣一層層連結下去就構成了我們通常所說的原型鏈。
我們通常會透過instanceof
這個運算子來判斷某個物件是不是某個建構函式的例項(一般我們認為一個物件的__proto__
屬性和某個建構函式的prototype
屬性指向同一地址instanceof
就會返回true
),a instanceof A
返回true,這樣看來似乎並沒與什麼不妥,但是我們發現a isntanceof Object
返回的也是true,a 是由Object建構函式的直接例項物件嗎?a的__proto__
和Object的prototype
指向同一地址嗎?顯然不是,那為什麼會返回true。其實instanceof
表明的是在a這個物件的原型鏈上存在一個物件的__proto__
屬性和某個建構函式的prototype
屬性指向的是同一地址(翻譯過來就是:a的整條[[prototype]]
鏈中是否出現過Object.prototype
)。a.__proto__.__proto__ === Object.prototype
這裡會返回true。其實要知道這樣的關係,我們還可以使用isPrototypeOf
Object.prototype.isPrototypeOf(a)
,當然也可以是b.isPrototypeOf(a)
在ES5中獲取物件的原型鏈標準方法是
Object.getPrototypeOf
,非標準方式是a.__proto__
(大多數現代瀏覽器都會支援)
但是如果這樣我們就不能判斷一個物件是不是某個建構函式的直接例項了,這時我們就可以使用constructor這個屬性
a.constructor === A //truea.constructor === Object // false
下面再來看看這張圖
image
我們從左上角說起,f2和f1是Foo建構函式的兩個例項,他們的__proto__
屬性指向Foo.prototype
所指向的地址(換句話說在這裡f2.__proto__
和Foo.prototype
同一個東西)。而Foo.prototype
也是一個物件,也擁有__proto__
屬性,這個屬性和Object.prototype
指向同一個地址,而Object.prototype.__porto__
指向null
(也就是說並不是每個物件都有__proto__
這個屬性)因為這已經是原型鏈的頂端了。我們再看建構函式Foo
其實也是一個物件(函式也是一個物件)它也擁有__proto__
,它的__proto__
屬性指向Function.prototype
所指向的地址(即Foo.__proto__ === Function.prototype
),這是因為函式物件都是有Function
這個建構函式構造的。
而Function.prototype
(或者Foo.__proto__.__proto__
)指向Object.prototype
。這裡還有中間的Object
這個特殊的建構函式,他是一個函式那麼他擁有prototype
屬性,同時他又是一個函式物件,那麼他就是由Function
構造出來,所以Object.__proto__ === Function.prototype
,Function
建構函式䦹如此。解釋起來有點麻煩,大家多看這個圖就好。所以就會出現下面這些題目了
Function instanceof Object // true Object instanceof Function // true Function instanceof Function //trueObject instanceof Object // trueNumber instanceof Number //false
上面說一個物件的__proto__
屬性指向對應建構函式的prototype
屬性所指向的地址,但是這裡如果我們新建的物件是透過Object.create函式建立,那麼新建立的這個物件的__proto__
會指向crate的引數
const p = {name: 'djlxs'};const o = Object(p);
即o.__proto__ === p
屬性遮蔽
當我們讀取某個物件的某個屬性時,實際上是透過[[Get]]這個操作,在物件本身沒有找到時,就會在其原型鏈上尋找直到找到或者返回undefined
,當一個屬性既出現在物件本身上,又出現在原型鏈上,那麼就會優先返回物件本身相應的屬性值,因此這裡就發生了屬性遮蔽
當我們向一個物件,新增某個屬性時,如果這個屬性存在於原型鏈上,且沒有設定成只讀,那麼會在這個物件本身新建這個屬性,從而遮蔽原型鏈上的相應屬性,但是如果原型鏈上的這個屬性設定成了只讀,那麼在嚴格模式下,會丟擲相應錯誤,非嚴格模式下,則會忽略。如果在這種情況下,想要設定這個屬性,那麼我們就不能直接使用=
這個賦值運算子,而是要使用Object.defineProperty()
在我們使用的要注意屬性遮蔽,這裡還有一種隱式的屬性遮蔽尤其要注意
var anotherObject = { a: 2}var myObject = Object.create(anotherObject); myObject++;console.log(anotherObject) // 2console.log(myObject) // 3
因為這裡myObject++
相當於myObject = myObject + 1;
注 以上參考自 《你不知道的JavaScript上卷》(144-146)
最後(歡迎大家關注我)
作者:DJL簫氏
連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2459/viewspace-2814141/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 關於原型和原型鏈的精闢解讀原型
- ? 圖解原型和原型鏈圖解原型
- 圖解原型和原型鏈圖解原型
- 再解 JavaScript 原型與原型鏈JavaScript原型
- JS原型與原型鏈圖解JS原型圖解
- javascript原型和原型鏈詳解JavaScript原型
- 原型與原型鏈原型
- 原型和原型鏈原型
- JavaScript 原型及原型鏈JavaScript原型
- JavaScript原型與原型鏈JavaScript原型
- JS原型和原型鏈JS原型
- 原型物件與原型鏈原型物件
- 原型和原型鏈梳理原型
- JavaScript 原型 與 原型鏈JavaScript原型
- 理解原型和原型鏈原型
- javascript——原型與原型鏈JavaScript原型
- JS 原型與原型鏈JS原型
- js原型及原型鏈JS原型
- JS原型與原型鏈JS原型
- JS原型鏈、prototype、__proto__、原型鏈繼承詳解JS原型繼承
- JavaScript從原型到原型鏈,細緻講解JavaScript原型
- js原型鏈圖解JS原型圖解
- js 原型鏈詳解JS原型
- 巧解 JS 原型鏈JS原型
- 理解js中的原型,原型物件,原型鏈JS原型物件
- JavaScript中的原型、原型鏈、原型模式JavaScript原型模式
- javascript之原型與原型鏈JavaScript原型
- JavaScript中原型與原型鏈JavaScript原型
- 白話原型和原型鏈原型
- JavaScript原型與原型鏈分析JavaScript原型
- 原型鏈原型
- 7. JavaScript 原型與原型鏈JavaScript原型
- 原型和原型鏈的深入探索原型
- 從感性角度談原型 / 原型鏈原型
- JS中的原型與原型鏈JS原型
- JavaScript系列之原型與原型鏈JavaScript原型
- 【前端面試】原型和原型鏈前端面試原型
- 淺談JavaScript原型及原型鏈JavaScript原型