首先咱們看上面的示意圖,咱們能夠看到每個構造函數都會有一個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.prototype
,Function
構造函數䦹如此。解釋起來有點麻煩,你們多看這個圖就好。因此就會出現下面這些題目了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__ === p
prototype
當咱們讀取某個對象的某個屬性時,其實是經過[[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)