1.原型(prototype)
JavaScript的每個(gè)函數(shù)都有一個(gè)prototype
屬性,它默認(rèn)指向一個(gè)空object實(shí)例對(duì)象口四。
我們可以在里面存放一些公用的屬性和方法。構(gòu)造函數(shù)的實(shí)例對(duì)象會(huì)自動(dòng)擁有這些定義在構(gòu)造函數(shù)原型對(duì)象中的屬性和方法。
function Student(name) {
this.name = name
}
Student.prototype.getName = function () {
return 'My name is ' + this.name
}
let s1 = new Student('jack')
console.log(s1.getName())
-----------------------------
>>>My name is jack
上面的例子中,Student
有一個(gè)屬性name
崇众。我們通過prototype
向Student
的原型中添加了一個(gè)叫getName()
的方法掂僵。
然后我們創(chuàng)建了Student
的實(shí)例對(duì)象s1
,并通過s1
執(zhí)行getName()
方法顷歌∶膛睿可以看到執(zhí)行成功并正確輸出了結(jié)果。
相對(duì)應(yīng)眯漩,原型對(duì)象中有一個(gè)屬性constructor
芹扭,它指向函數(shù)對(duì)象。通過prototype
和constructor
赦抖,構(gòu)造函數(shù)和它的原型對(duì)象實(shí)現(xiàn)了相互引用舱卡。
2.顯式原型和隱式原型
每個(gè)函數(shù)function都有一個(gè)prototype
屬性,這就是顯式原型队萤。每個(gè)實(shí)例對(duì)象都有一個(gè)__proto__
屬性轮锥,稱為隱式原型。
- 函數(shù)的
prototype
屬性是函數(shù)定義的時(shí)候自動(dòng)添加的要尔,默認(rèn)指向一個(gè)空object實(shí)例對(duì)象舍杜。 - 對(duì)象的
__proto__
屬性是創(chuàng)建對(duì)象的時(shí)候自動(dòng)添加的,默認(rèn)值是構(gòu)造函數(shù)的prototype屬性的值赵辕。
在內(nèi)存中既绩,它們的關(guān)系如下圖所示
當(dāng)執(zhí)行
Student
函數(shù)定義的時(shí)候,堆(Heap)中產(chǎn)生Student
函數(shù)的對(duì)象(假設(shè)地址為0x111)还惠,同時(shí)在棧(stack)中劃出一塊區(qū)域存儲(chǔ)此地址饲握。然后JavaScript又在堆中生成一個(gè)空Object對(duì)象(假設(shè)地址為0x333),并將地址值存儲(chǔ)在
Student
函數(shù)對(duì)象的prototype
屬性里吸重。
當(dāng)創(chuàng)建Student
的實(shí)例對(duì)象s1
時(shí)互拾,堆中產(chǎn)生s1
實(shí)例對(duì)象(假設(shè)地址為0x222),同時(shí)在棧(stack)中劃出一塊區(qū)域存儲(chǔ)此地址嚎幸。而s1
實(shí)例對(duì)象有一個(gè)__proto__
屬性颜矿,初識(shí)值也被設(shè)置為了0x333
。
所以在上面的例子中嫉晶,Student.prototype
和s1.__proto__
指向的完全是同一個(gè)對(duì)象骑疆。通過代碼可以驗(yàn)證這一點(diǎn)。
console.log(Student.prototype === s1.__proto__)
-----------------------------
>>>true
這就解釋了為什么s1
實(shí)例對(duì)象可以訪問到添加在Student
的原型中的方法替废。因?yàn)槲覀兺ㄟ^prototype
向Student
的原型中添加方法時(shí)箍铭,方法實(shí)際是被添加到了地址0x333的object
對(duì)象里面。
Student.prototype.getName = function () {
return 'My name is ' + this.name
}
當(dāng)實(shí)例對(duì)象s1
嘗試執(zhí)行getName()
方法時(shí)椎镣,會(huì)首先在地址0x222的自身中查找該方法诈火。
由于s1
中沒有getName()
方法,所以讀取__proto__
屬性的值状答,自動(dòng)往上到地址為0x333的object
對(duì)象里去尋找冷守。
3.原型鏈
訪問一個(gè)對(duì)象的屬性時(shí)刀崖,會(huì)先在自身的屬性中查找,如果找到了就返回該屬性拍摇。如果沒有找到就會(huì)沿著__proto__
這條鏈向上查找亮钦,一旦找到,就返回該屬性充活。如果最終都沒有找到蜂莉,則返回undefined。
像這樣一條抽象的鏈條混卵,就是原型鏈映穗。因?yàn)樵玩準(zhǔn)怯?code>__proto__屬性連接起來的,所以也叫隱式原型鏈幕随。
下面是一個(gè)例子:
function Student(name, age) {
this.name = name
this.age = age
this.getAge = function () {
return 'My age is ' + this.age
}
}
Student.prototype.getName = function () {
return 'My name is ' + this.name
}
let s1 = new Student('jack', 10)
console.log(s1.getAge())
console.log(s1.getName())
console.log(s1.toString())
console.log(s1.getAddress())
-----------------------------
>>>My age is 10
>>>My name is jack
>>>[object Object]
>>>TypeError: s1.getAddress is not a function
例子中有兩個(gè)方法getAge()
和getName()
男公,分別定義在Student
自身以及Student
的原型上。然后創(chuàng)建了Student
的實(shí)例對(duì)象s1
并依次執(zhí)行getAge()
合陵,getName()
枢赔,toString()
和getAddress()
這4個(gè)函數(shù),輸出結(jié)果拥知。
-
getAge()
:在s1
自身的屬性里找到然后執(zhí)行踏拜。 -
getName()
:在s1
自身的屬性里無法找到,根據(jù)__proto__
的值到Student
的原型對(duì)象中查找低剔,找到然后執(zhí)行速梗。 -
toString()
:在s1
以及Student
的原型對(duì)象中都無法找到,進(jìn)一步根據(jù)Student
的原型對(duì)象的__proto__
的值查找Student
原型對(duì)象的原型對(duì)象(也就是Object的原型對(duì)象)襟齿。在里面找到toString()
函數(shù)(JavaScript提供)然后執(zhí)行姻锁。 -
getAddress()
:在s1
,Student
的原型對(duì)象猜欺,Object的原型對(duì)象中都無法找到位隶,所以返回undefined。例子中把返回的undefined作為函數(shù)來執(zhí)行开皿,所以系統(tǒng)輸出了TypeError
錯(cuò)誤信息涧黄。
它們?cè)趦?nèi)存中的關(guān)系如下圖所示:
4.原型鏈的經(jīng)典結(jié)構(gòu)圖
這是一張流行已久的神圖,它是如此經(jīng)典以至于只要講原型鏈赋荆,基本都得把它拿出來展示一下笋妥。
圖上的一部分內(nèi)容其實(shí)已經(jīng)在前面的例子中已經(jīng)說明過了,下面是一些補(bǔ)充內(nèi)容窄潭。
- 構(gòu)造函數(shù)
Foo()
除了有prototype
屬性春宣,其實(shí)也是有__proto__
屬性的。這其實(shí)很好理解,因?yàn)?code>Foo()也是一個(gè)對(duì)象月帝。我們?cè)诙x函數(shù)對(duì)象function Foo(){}
的時(shí)候其實(shí)相當(dāng)于執(zhí)行了var Foo = new Function(){}
材义。只要是對(duì)象就有__proto__
屬性。 - 一般而言嫁赏,函數(shù)對(duì)象的
prototype
屬性指向一個(gè)空object對(duì)象,而它們的__proto__
屬性都指向Function
對(duì)象的原型油挥。但有兩個(gè)函數(shù)對(duì)象是例外潦蝇。-
Function
函數(shù)對(duì)象的prototype
屬性和__proto__
屬性均指向Function
對(duì)象的原型。這是因?yàn)?code>Function函數(shù)對(duì)象是由自身創(chuàng)建的深寥,它是自身的實(shí)例對(duì)象攘乒。 -
Object
函數(shù)對(duì)象的prototype
屬性并不指向一個(gè)空object實(shí)例對(duì)象,它直接指向Object的原型對(duì)象惋鹅。
-
-
Object的原型對(duì)象是原型鏈的盡頭则酝。它的
__proto__
屬性的值為null。