此文章幾乎是來(lái)自大牛mqyqingfeng,畢竟人家總結(jié)的直觀又更容易理解
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
// 雖然寫在注釋里,但是你要注意:
// prototype是函數(shù)才會(huì)有的屬性
該函數(shù)的prototype指向調(diào)用構(gòu)造函數(shù)而創(chuàng)建出來(lái)的實(shí)例的原型(對(duì)象)浦马,也就是例子中的person1息堂,person2.故我們修改Person函數(shù)的prototype屬性宣蠕,實(shí)例化的對(duì)象能夠訪問(wèn)。
__proto__
這是每一個(gè)JavaScript對(duì)象(除了 null )都具有的一個(gè)屬性阔逼,叫 __proto__,
這個(gè)屬性會(huì)指向該對(duì)象的原型
console.log(person.__proto__ === Person.prototype); // true
于是我們更新下關(guān)系圖
理解為該構(gòu)造函數(shù)實(shí)例化出來(lái)的對(duì)象的__proto__指向該構(gòu)造函數(shù)的prototype(也就是該對(duì)象person1地沮,person2的原型)
問(wèn)題:既然實(shí)例對(duì)象和構(gòu)造函數(shù)都可以指向原型嗜浮,那么原型是否有屬性指向構(gòu)造函數(shù)或者實(shí)例呢?
答案:指向?qū)嵗經(jīng)]有摩疑,因?yàn)橐粋€(gè)構(gòu)造函數(shù)可以生成多個(gè)實(shí)例危融。但原型指向構(gòu)造函數(shù)還是有的,
接下來(lái)就是我們要提到的第三個(gè)屬性constructor雷袋,每個(gè)原型都有一個(gè) constructor 屬性指向關(guān)聯(lián)的構(gòu)造函數(shù)吉殃。
constructor
console.log(Person === Person.prototype.constructor); // true
所以再更新下關(guān)系圖:
綜上我們已經(jīng)得出:
function Person() { }
var person = new Person();
console.log(person.__proto__ == Person.prototype) // true
console.log(Person.prototype.constructor == Person) // true
// 順便學(xué)習(xí)一個(gè)ES5的方法,可以獲得對(duì)象的原型
console.log(Object.getPrototypeOf(person) === Person.prototype) // true
二丶實(shí)例與原型
當(dāng)讀取實(shí)例的屬性時(shí),如果找不到,就會(huì)查找與對(duì)象關(guān)聯(lián)的原型中的屬性蛋勺,如果還查不到瓦灶,就去找原型的原型,一直找到最頂層為止迫卢。
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(對(duì)象關(guān)聯(lián)的原型中的name屬性)
從 person 對(duì)象中找不到 name 屬性就會(huì)從 person 的原型也就是 person.__proto__ 倚搬,也就是 Person.prototype中查找,要是再找不到就去原型的原型去查找,那什么是原型的原型呢乾蛤?
我們理解原型是一個(gè)對(duì)象每界,那么對(duì)象都是由原始new Object()
來(lái)創(chuàng)建的(原型對(duì)象就是通過(guò) Object 構(gòu)造函數(shù)生成的),結(jié)合之前所講家卖,實(shí)例的__proto__指向構(gòu)造函數(shù)的 prototype 眨层,所以我們?cè)俑孪玛P(guān)系圖:
那 Object.prototype 的原型呢?
console.log(Object.prototype.__proto__ === null) // true
順便還要說(shuō)一下上荡,圖中由相互關(guān)聯(lián)的原型組成的鏈狀結(jié)構(gòu)就是原型鏈趴樱,也就是的這條線。
面試題:
請(qǐng)問(wèn)你如何理解JavaScript原型鏈酪捡?
- 1.JavaScript中每個(gè)對(duì)象都有一個(gè)prototype屬性叁征,我們稱之為原型,而原型的值也是一個(gè)對(duì)象逛薇,因此它也有自己的原型捺疼,這樣就串聯(lián)起來(lái)了一條原型鏈,原型鏈的鏈頭是object永罚,它的prototype.__proto__啤呼,值為null。
- 2.原型鏈的作用是用于對(duì)象繼承呢袱,函數(shù)A的原型屬性(prototype property)是一個(gè)對(duì)象官扣,當(dāng)這個(gè)函數(shù)被用作函數(shù)來(lái)創(chuàng)建實(shí)例時(shí),該函數(shù)的原型屬性被作為原型賦值給所有對(duì)象實(shí)例羞福,比如我們新建一個(gè)數(shù)組惕蹄,數(shù)組的方法便從數(shù)組的原型上繼承而來(lái)。
- 3.當(dāng)訪問(wèn)對(duì)象的一個(gè)屬性時(shí)坯临,首先查找對(duì)象本身焊唬,找到則返回;未找到則繼續(xù)查找原型對(duì)象的屬性(如果還找不到實(shí)際上還會(huì)沿著原型鏈向上查找看靠,直至到根)赶促。只要沒(méi)有被覆蓋的話,對(duì)象原型的屬性就能在所有的實(shí)例中找到挟炬,若整個(gè)原型鏈未找到則返回undefined鸥滨。
補(bǔ)充
最后嗦哆,補(bǔ)充三點(diǎn)大家可能不會(huì)注意的地方:
- constructor
首先是 constructor 屬性,我們看個(gè)例子:
function Person() { }
var person = new Person();
console.log(person.constructor === Person); // true
當(dāng)獲取 person.constructor 時(shí)婿滓,其實(shí) person 中并沒(méi)有 constructor 屬性,當(dāng)不能讀取到constructor 屬性時(shí)老速,會(huì)從 person 的原型也就是 Person.prototype 中讀取,正好原型中有該屬性凸主,所以:
person.constructor === Person.prototype.constructor
- __proto__
其次是__proto__ 橘券,絕大部分瀏覽器都支持這個(gè)非標(biāo)準(zhǔn)的方法訪問(wèn)原型,然而它并不存在于 Person.prototype 中卿吐,實(shí)際上旁舰,它是來(lái)自于 Object.prototype ,與其說(shuō)是一個(gè)屬性嗡官,不如說(shuō)是一個(gè) getter/setter箭窜,當(dāng)使用 obj.__proto__ 時(shí),可以理解成返回了 Object.getPrototypeOf(obj)衍腥。
- 真的是繼承嗎磺樱?
最后是關(guān)于繼承,前面我們講到“每一個(gè)對(duì)象都會(huì)從原型‘繼承’屬性”婆咸,實(shí)際上竹捉,繼承是一個(gè)十分具有迷惑性的說(shuō)法,引用《你不知道的JavaScript》中的話尚骄,就是:
繼承意味著復(fù)制操作活孩,然而 JavaScript 默認(rèn)并不會(huì)復(fù)制對(duì)象的屬性,相反乖仇,JavaScript 只是在兩個(gè)對(duì)象之間創(chuàng)建一個(gè)關(guān)聯(lián),這樣询兴,一個(gè)對(duì)象就可以通過(guò)委托訪問(wèn)另一個(gè)對(duì)象的屬性和函數(shù)乃沙,所以與其叫繼承,委托的說(shuō)法反而更準(zhǔn)確些诗舰。