Javascript之旅 對(duì)象的原型鏈之由來
function Base(){}var base = new Base()
上面兩行代碼會(huì)創(chuàng)建幾個(gè)對(duì)象(object)?
要回答這個(gè)問題,先明確一下Javascript里object的概念。
Objects
在Javascript里,幾乎一切都是object(Arrays、Functions、Numbers、Objects……),而沒有C#里的class的概念。object的本質(zhì)是一個(gè)name-value pairs的集合,其中name是string類型的,可以把它叫做“property”,value包括各種objects(string,number,boolean,array,function…),指的是property的值。
typeof
既然object包含Arrays、Functions、Numbers、Objects……,那怎么區(qū)分這些呢?答案是typeof。 typeof返回一個(gè)字符串,如typeof(Null) = “object”,typeof(false) = “Boolean”, typeof(1) = “number”。既然總是返回字符串,那么對(duì)于typeof (typeof x),不管x是什么,總是返回”string”。

Constructor
JS里沒有class,也就沒有class里的構(gòu)造函數(shù),那么object是怎么被創(chuàng)建的呢?用構(gòu)造器:constructor。constructor其實(shí)就是Function,因此本身也是object。開頭的function Base(){}就是一個(gè)構(gòu)造器,var b = new Base()就是用這個(gè)構(gòu)造器(通過關(guān)鍵字new)創(chuàng)建了一個(gè)叫b的object。至此我們可以得出結(jié)論,開頭的兩行代碼至少創(chuàng)建了2個(gè)object:一個(gè)是Base,類型為function的object,一個(gè)是base,類型為object的object。
Function()和Object()
這是兩個(gè)重要的預(yù)定義好的構(gòu)造器。一切function(比如開頭的Base())都是由Function()構(gòu)造出來的;而Object的prototype將會(huì)被所有object繼承,下面會(huì)講到。
Function的創(chuàng)建過程
當(dāng)執(zhí)行function Base(){this.a = 1}時(shí),相當(dāng)于var Base = new Function(“this.a = 1”),也就是說,這行代碼本身,將使用預(yù)定義好的Function() constructor,來構(gòu)造一個(gè)function型object(即Base)出來。在這個(gè)創(chuàng)建過程中,js將做哪些事呢?
1, 首先當(dāng)然會(huì)創(chuàng)建一個(gè)object起來,Base指向這個(gè)object。typeof 這個(gè)object = “function”
2, 給Base附上__proto__屬性,讓它等于Function這個(gè)構(gòu)造器的prototype(也是預(yù)定義好的)。這是很重要的一步,也是規(guī)律性的一步。(規(guī)律:)在執(zhí)行任意類似varx = new X()時(shí),都會(huì)把X的prototype賦給x的__proto__,也就是說,x.__proto__和X.prototype此時(shí)會(huì)指向同一個(gè)對(duì)象。
3, 為Base創(chuàng)建call屬性,該屬性是個(gè)function。因此我們可以這樣寫:Base.Call()
4, 為Base創(chuàng)建Construct屬性,該屬性也是個(gè)function。在執(zhí)行var base = new Base()時(shí),即會(huì)調(diào)用這個(gè)Construct屬性。
5, 為Base創(chuàng)建Scope,Length等屬性,略。
6, 為Base創(chuàng)建prototype屬性:先用new Object()創(chuàng)建一個(gè)對(duì)象,為這個(gè)對(duì)象創(chuàng)建一個(gè)屬性叫constructor,該屬性值設(shè)置為Base。再把Base的prototype設(shè)置為這個(gè)新創(chuàng)建的對(duì)象。偽代碼如下:
var x = new Object();
x.constructor = Base;
Base.prototype = x;
先把關(guān)注點(diǎn)放到2和6。
__proto__和prototype
從2可以看出來,任意一個(gè)用構(gòu)造器構(gòu)造出來的object(包括Objects和Functions),都會(huì)有__proto__屬性,指向該構(gòu)造器的prototype屬性。注意__proto__是個(gè)私有屬性,在IE上是看不到的,我用的是chrome,可以看到。
從6可以看出,任意一個(gè)用new Function()構(gòu)造出來的functions,都會(huì)有prototype屬性,該屬性是用new Object()構(gòu)建出來的,初始公開屬性只有一個(gè)constructor。
原型鏈
再來分析下第6步的偽代碼,也就是為function創(chuàng)建prototype的這一步:
var x = new Object(); // 參見2中的規(guī)律,會(huì)有x.__proto__= Object.prototype。
x.constructor = Base;
Base.prototype = x;
此時(shí)我們用Base()構(gòu)造一個(gè)對(duì)象出來:
var base= new Base(); // 參見2中的規(guī)律,會(huì)有base.__proto__ = Base.prototype,也就是 = x。// 因此有base.__proto__.__proto__ = x.__proto__// 而x.__proto__ = Object.prototype(見上一個(gè)代碼片段)// 所以,base.__proto__.__proto__ = Object.prototype.
__proto__.__proto__,這就是傳說中JS對(duì)象的原型鏈!由于用Function()創(chuàng)建構(gòu)造器時(shí)的關(guān)鍵的第6步,保證了所有object的原型鏈的頂端,最終都指向了Object.prototype。
Property Lookup
而我們?nèi)绻x某個(gè)object的某個(gè)屬性,JS會(huì)怎么做呢?
比如有個(gè)object叫xxx,我們執(zhí)行alert(xxx.a),也就是讀取xxx的a屬性,那么JS首先會(huì)到xxx本身去找a屬性,如果沒找到,則到xxx.__proto__里去找a屬性,由此沿著原型鏈往上,找到即返回(沒找到,則返回undefined)??梢詠砜磦€(gè)例子:
上圖得知:base本身是沒有constructor屬性的,但是base.constructor確實(shí)能返回Base這個(gè)函數(shù),原因就在于base.__proto__有這個(gè)屬性。(base.__proto__是啥?就是Base.prototype,上面構(gòu)建Function的第6步的偽代碼里,為Base.prototype.constructor賦值為Base本身。)
Object作為“基類”
另外,由于任意object的原型鏈的頂端都是Object.prototype。所以,Object.prototype里定義的屬性,就會(huì)通過原型鏈,被所有的object繼承下來。這樣,預(yù)定義好的Object,就成了所有對(duì)象的“基類”。這就是原型鏈的繼承。
看上圖,Object.prototype已經(jīng)預(yù)定義好了一些屬性,我們?cè)僮芳右粭l屬性叫propertyA,那么這個(gè)屬性和預(yù)定義屬性一樣,都可以從base上讀到。
原型繼承
已經(jīng)得知,
對(duì)于 var xxx =new Object(); 有xxx.__proto__= Object.prototype;
對(duì)于 var xxx =new Base(); 有xxx.__proto__.__proto__= Object.prototype;
看上去很像什么呢?從c#角度看,很像Base是Object的子類,也就是說,由Base()構(gòu)造出來的object,比由Object()構(gòu)造出來的object,在原型鏈上更低一個(gè)層級(jí)。這是通過把Base.prototype指向由Object()創(chuàng)建的對(duì)象來做到的。那么自然而然,如果我想定義一個(gè)繼承自Base的構(gòu)造器,只需把改構(gòu)造器的prototype指向一個(gè)Base()構(gòu)造出來的對(duì)象。
function Derived(){}
var base = new Base();
Derived.prototype = base;
var d = newDerived(); //很容易推算出:d.__proto__.__proto__.__proto__ = Object.prototype.
推算過程:d.__proto__指向Derived.prototype,也就是base;則__proto__.__proto__指向base.__proto__,也就是Base.prototype,也就是某個(gè)new object()創(chuàng)建出來的東東,假設(shè)是o;則__proto__.__proto__.__proto__指向o.__proto__,也就是Object.prototype。
回答開頭的問題,以及幾個(gè)新的問題
那兩行代碼至少創(chuàng)建了三個(gè)對(duì)象:Base、base、Base.prototype。順便說說,base是沒有prototype屬性的,只有function類型的object,在被構(gòu)建時(shí)才會(huì)被創(chuàng)建prototype屬性。
d.constructor會(huì)返回什么呢?
構(gòu)造器Base()和Derived()里都是空的,如果有代碼,將會(huì)怎么被執(zhí)行呢?
……
待續(xù)。見下篇
相關(guān)文章
js重寫alert事件(避免alert彈框標(biāo)題出現(xiàn)網(wǎng)址)
這篇文章主要給大家介紹了關(guān)于js重寫alert事件的相關(guān)資料,這樣可以避免alert彈框標(biāo)題出現(xiàn)網(wǎng)址的情況,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12關(guān)于js new Date() 出現(xiàn)NaN 的分析
在一個(gè)項(xiàng)目中需要進(jìn)行日期的格式化,后臺(tái)傳到前端是時(shí)間的整數(shù)(Date.getTime),當(dāng)后臺(tái)數(shù)據(jù)返回字符串時(shí),發(fā)現(xiàn)轉(zhuǎn)換日期時(shí)在ie下變成NaN,但是真的是這樣嗎?接下來我們慢慢分析2012-10-10測(cè)量JavaScript函數(shù)的性能各種方式對(duì)比
這篇文章主要介紹了測(cè)量JavaScript函數(shù)的性能各種方式對(duì)比,對(duì)性能感興趣的同學(xué),可以多實(shí)驗(yàn)一下2021-04-04淺談layui使用模板引擎動(dòng)態(tài)渲染元素要注意的問題
今天小編就為大家分享一篇淺談layui使用模板引擎動(dòng)態(tài)渲染元素要注意的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-09-09手機(jī)端點(diǎn)擊圖片放大特效PhotoSwipe.js插件實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了手機(jī)端點(diǎn)擊圖片放大特效PhotoSwipe.js插件實(shí)現(xiàn)方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-08-08JavaScript實(shí)現(xiàn)的浮動(dòng)層框架用法實(shí)例分析
這篇文章主要介紹了JavaScript實(shí)現(xiàn)的浮動(dòng)層框架用法,以實(shí)例形式分析了JavaScript實(shí)現(xiàn)可關(guān)閉的半透明浮動(dòng)層相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10