徹底理解JavaScript的原型與原型鏈
前言
原型與原型鏈知識(shí)歷來都是面試中考察的重點(diǎn),說難不算太難,但要完全理解還是得下一定的功夫。先來看一道面試題開開胃口吧:
function User() {} User.prototype.sayHello = function() {} var u1 = new User(); var u2 = new User(); console.log(u1.sayHello === u2.sayHello); console.log(User.prototype.constructor); console.log(User.prototype === Function.prototype); console.log(User.__proto__ === Function.prototype); console.log(User.__proto__ === Function.__proto__); console.log(u1.__proto__ === u2.__proto__); console.log(u1.__proto__ === User.__proto__); console.log(Function.__proto__ === Object.__proto__); console.log(Function.prototype.__proto__ === Object.prototype.__proto__); console.log(Function.prototype.__proto__ === Object.prototype);
基礎(chǔ)鋪墊
JavaScript所有的對(duì)象本質(zhì)上都是通過new 函數(shù)創(chuàng)建的,包括對(duì)象字面量的形式定義對(duì)象(相當(dāng)于new Object()的語法糖)。
所有的函數(shù)本質(zhì)上都是通過new Function創(chuàng)建的,包括Object、Array等
所有的函數(shù)都是對(duì)象。
prototype
每個(gè)函數(shù)都有一個(gè)屬性prototype,它就是原型,默認(rèn)情況下它是一個(gè)普通Object對(duì)象,這個(gè)對(duì)象是調(diào)用該構(gòu)造函數(shù)所創(chuàng)建的實(shí)例的原型。
contructor屬性
JavaScript同樣存在由原型指向構(gòu)造函數(shù)的屬性:constructor,即Func.prototype.constructor --> Func
__proto__
JavaScript中所有對(duì)象(除了null)都具有一個(gè)__proto__屬性,該屬性指向該對(duì)象的原型。
function User() {} var u1 = new User(); // u1.__proto__ -> User.prototype console.log(u1.__proto__ === User.prototype) // true
顯而易見,實(shí)例的__proto__屬性指向了構(gòu)造函數(shù)的原型,那么多個(gè)實(shí)例的__proto__會(huì)指向同一個(gè)原型嗎?
var u2 = new User(); console.log(u1.__proto__ === u2.__proto__) // true
如果多個(gè)實(shí)例的__proto__都指向構(gòu)造函數(shù)的原型,那么實(shí)例如果能通過一種方式,訪問原型上的方法,屬性等,就可以在原型進(jìn)行編程,實(shí)現(xiàn)繼承的效果。
我們繼續(xù)更新一下原型與原型鏈的關(guān)系圖:
原型鏈
實(shí)例對(duì)象在查找屬性時(shí),如果查找不到,就會(huì)沿著__proto__去與對(duì)象關(guān)聯(lián)的原型上查找,如果還查找不到,就去找原型的原型,直至查到最頂層,這也就是原型鏈的概念。
就借助面試題,舉幾個(gè)原型鏈的例子:
舉例
- u1.sayHello():
u1上是沒有sayHello方法的,因此訪問u1.__proto__(User.prototype),成功訪問到sayHello方法 - u2.toString()
u2,User.prototype都沒有toString方法,User.prototype也是一個(gè)普通對(duì)象,因此繼續(xù)尋找User.prototype.__proto__(Object.prototype),成功調(diào)用到toString方法
提高
學(xué)完上面那些,大多數(shù)面試題都可以做出來了,例如下面這種
function A() {} function B(a) { this.a = a; } function C(a) { if (a) { this.a = a; } } A.prototype.a = 1; B.prototype.a = 1; C.prototype.a = 1; console.log(new A().a); //1 console.log(new B().a); //undefined console.log(new C(2).a); //2
但距離解決文章的最初的面試題還欠缺些什么,比如Function.__proto__ === Object.__proto__、Function.prototype.__proto__ === Object.prototype.__proto__等,接著我們來一一攻克它。
Objcet.__proto__ 、 Object.prototype、Object.prototype.__proto__
- Object是構(gòu)造函數(shù),在第二部分我們講過所有的函數(shù)都是通過new Function創(chuàng)建了,因此Object相當(dāng)于Function的實(shí)例,即Object.__proto__ --> Function.prototype。
- Object.prototype是Object構(gòu)造函數(shù)的原型,處于原型鏈的頂端,Object.prototype.__proto__已經(jīng)沒有可以指向的上層原型,因此其值為null
// 總結(jié): Object.__proto__ --> Function.prototype Object.prototype.__proto__ --> null
Function.__proto__、Function.prototype、Function.prototype.__proto__
- Function.prototype是Function的原型,是所有函數(shù)實(shí)例的原型,例如上面講的Object.__proto__
- Function.prototype是一個(gè)普通對(duì)象,因此Function.prototype.__proto__ --> Object.prototype
- Function.__proto__: __proto__指向創(chuàng)造它的構(gòu)造函數(shù)的原型,那誰創(chuàng)造了Function那?
- 猜想:函數(shù)對(duì)象也是對(duì)象,那Function.__proto__會(huì)指向Object.prototype嗎?上文提到,Object.__proto__ --> Function.prototype。如果Function.__proto__ -> Object.prototype,感覺總是怪怪的,到底誰創(chuàng)造了誰,于是我去做了一下測試:
實(shí)踐證明只存在Object.__proto__ --> Function.prototype
苦思冥想沒得出結(jié)果,難道Function函數(shù)是猴子不成,從石頭縫里面蹦出來的?于是我進(jìn)行了一同亂七八糟的測試,沒想到找出了端倪。
通過上面我們可以得出:Function.__proto__ --> Function.prototype
Function函數(shù)不通過任何東西創(chuàng)建,JS引擎啟動(dòng)時(shí),添加到內(nèi)存中
總結(jié)
最后將原型與原型鏈方面的知識(shí)凝結(jié)成一張圖:
- 所有函數(shù)(包括Function)的__proto__指向Function.prototype
- 自定義對(duì)象實(shí)例的__proto__指向構(gòu)造函數(shù)的原型
- 函數(shù)的prototype的__proto__指向Object.prototype
- Object.prototype.__proto__ --> null
后語
知識(shí)的海洋往往比想象中還要遼闊,原型與原型鏈這邊也反復(fù)的學(xué)過多次,我認(rèn)為應(yīng)該學(xué)的比較全面,比較完善了。但遇到這個(gè)面試題后,我才發(fā)現(xiàn)我所學(xué)的只不過是一根枝干,JS里面真的有很多深層次的寶藏等待挖掘。學(xué)海無涯,與君共勉。
最后再附贈(zèng)個(gè)簡單的面試題,提高一下自信:
var F = function () {} Object.prototype.a = function () {} Function.prototype.b = function () {} var f = new F(); console.log(f.a, f.b, F.a, F.b); // 原型鏈 // f.__proto__ --> F.prototype --> Object.prototype // F.__proto__ --> Function.prototype --> Object.prototype
到此這篇關(guān)于JavaScript原型與原型鏈的文章就介紹到這了,更多相關(guān)JavaScript原型與原型鏈內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JS的遞增/遞減運(yùn)算符和帶操作的賦值運(yùn)算符的等價(jià)式
JS的遞增/遞減運(yùn)算符和帶操作的賦值運(yùn)算符的等價(jià)式...2007-12-12js,jQuery 排序的實(shí)現(xiàn)代碼,網(wǎng)頁標(biāo)簽排序的實(shí)現(xiàn),標(biāo)簽排序
js,jQuery 排序的實(shí)現(xiàn),網(wǎng)頁標(biāo)簽排序的實(shí)現(xiàn),標(biāo)簽排序,需要的朋友可以參考下。2011-04-04js實(shí)現(xiàn)日期顯示的一些操作(實(shí)例講解)
下面小編就為大家?guī)硪黄猨s實(shí)現(xiàn)日期顯示的一些操作(實(shí)例講解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-07-07網(wǎng)絡(luò)復(fù)制內(nèi)容時(shí)常用的正則+editplus
有時(shí)侯我們在拷貝網(wǎng)頁上的內(nèi)容的時(shí)候,總是有一些,開頭的數(shù)字,需要替換掉2006-11-11JS Range HTML文檔/文字內(nèi)容選中、庫及應(yīng)用介紹
本文的內(nèi)容基本上是基于“區(qū)域范圍對(duì)象(Range objects)”這個(gè)概念來說的2011-05-05用javascript實(shí)現(xiàn)的支持lrc歌詞的播放器
用javascript實(shí)現(xiàn)的支持lrc歌詞的播放器...2007-05-05JavaScript+html5 canvas繪制繽紛多彩的三角形效果完整實(shí)例
這篇文章主要介紹了JavaScript+html5 canvas繪制繽紛多彩的三角形效果,以完整實(shí)例形式分析了html5的canvas繪制圖形的相關(guān)技巧,需要的朋友可以參考下2016-01-01