原型&原型鏈深度解讀

基本概念

原型鏈

首先咱們看上面的示意圖,咱們能夠看到每個構造函數都會有一個prototype屬性(js引擎自動幫咱們加上的),這個屬性會指向一個原型對象,這個構造函數經過new 會生成一個新的實例對象,這個實例擁有一個__prto__屬性,而這個屬性也會指向原型對象。javascript

function A () {} // 構造函數
const a = new A(); // 實例

A.prototye === a.__proro__  // true
複製代碼

也就是說一般狀況下(沒有手動給A.prototye從新賦值)構造函數的prototype和實例的__proto__指向同一地址。而原型對象會有constructor屬性指向這個構造函數。一樣原型對象也是一個對象,這個對象也會有他的__proto__屬性,這個屬性又會指向另外一個原型對象,這樣一層層連接下去就構成了咱們一般所說的原型鏈。java

咱們一般會經過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)git

在ES5中獲取對象的原型鏈標準方法是Object.getPrototypeOf,非標準方式是a.__proto__(大多數現代瀏覽器都會支持)github

可是若是這樣咱們就不能判斷一個對象是否是某個構造函數的直接實例了,這時咱們就可使用constructor這個屬性瀏覽器

a.constructor === A //true
a.constructor === Object // false
複製代碼

下面再來看看這張圖 函數

咱們從左上角提及,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.prototypeFunction構造函數䦹如此。解釋起來有點麻煩,你們多看這個圖就好。因此就會出現下面這些題目了ui

Function instanceof Object // true 
Object instanceof Function // true 
Function instanceof Function //true
Object instanceof Object // true
Number instanceof Number //false
複製代碼

上面說一個對象的__proto__屬性指向對應構造函數的prototype屬性所指向的地址,可是這裏若是咱們新建的對象是經過Object.create函數建立,那麼新建立的這個對象的__proto__會指向crate的參數spa

const p = {name: 'djlxs'};
const o = Object(p);
複製代碼

o.__proto__ === pprototype

屬性屏蔽

當咱們讀取某個對象的某個屬性時,其實是經過[[Get]]這個操做,在對象自己沒有找到時,就會在其原型鏈上尋找直到找到或者返回undefined,當一個屬性既出如今對象自己上,又出如今原型鏈上,那麼就會優先返回對象自己相應的屬性值,所以這裏就發生了屬性屏蔽翻譯

當咱們向一個對象,添加某個屬性時,若是這個屬性存在於原型鏈上,且沒有設置成只讀,那麼會在這個對象自己新建這個屬性,從而屏蔽原型鏈上的相應屬性,可是若是原型鏈上的這個屬性設置成了只讀,那麼在嚴格模式下,會拋出相應錯誤,非嚴格模式下,則會忽略。若是在這種狀況下,想要設置這個屬性,那麼咱們就不能直接使用=這個賦值操做符,而是要使用Object.defineProperty()

在咱們使用的要注意屬性屏蔽,這裏還有一種隱式的屬性屏蔽尤爲要注意

var anotherObject = {
	a: 2
}

var myObject = Object.create(anotherObject);

myObject++;

console.log(anotherObject)  // 2
console.log(myObject) // 3
複製代碼

由於這裏myObject++至關於myObject = myObject + 1;

注 以上參考自 《你不知道的JavaScript上卷》(144-146)

最後(歡迎你們關注我)

DJL簫氏我的博客

博客GitHub地址

簡書

掘金

相關文章
相關標籤/搜索