理解JS中的原型

動(dòng)態(tài)語言和靜態(tài)語言有很大的不同,比如在C++中定義類時(shí),并不分配內(nèi)存,而在動(dòng)態(tài)語言中定義類時(shí),卻會(huì)分配內(nèi)存。

  • 比如在JS中定義了一個(gè)函數(shù)時(shí),將會(huì)為該函數(shù)創(chuàng)建一個(gè)prototype屬性,這個(gè)屬性指向該函數(shù)的原型對(duì)象;JS中萬物皆對(duì)象,一個(gè)對(duì)象要么是函數(shù)的實(shí)例,要么是原型的實(shí)例。
  • 比如在Python中定義了一個(gè)類時(shí),將會(huì)創(chuàng)建一個(gè)類型對(duì)象(類其實(shí)是能夠創(chuàng)建出類實(shí)例的對(duì)象,類本身也是實(shí)例,而且是metaclass元類的實(shí)例);Python中所有的東西都是對(duì)象,其要么是類的實(shí)例,要么是metaclass元類的實(shí)例。

原型對(duì)象中的屬性被所有實(shí)例所共享,這類似于C++中的靜態(tài)成員,靜態(tài)成員屬于類本身,而不是屬于對(duì)象,但是被類的所有實(shí)例所共有。

創(chuàng)建一個(gè)空函數(shù)

function Person() {};

像這樣創(chuàng)建一個(gè)空函數(shù),js解析為以下三步:

  1. 創(chuàng)建一個(gè)Object對(duì)象(有constructor屬性及[[Prototype]]屬性);
  2. 創(chuàng)建一個(gè)函數(shù)(有name、prototype屬性),再通過prototype屬性引用剛才創(chuàng)建的對(duì)象;
  3. 創(chuàng)建變量Person,同時(shí)把函數(shù)的引用賦值給變量Person


    如圖可以表示為

實(shí)例化一個(gè)對(duì)象

我們用上面這個(gè)Person函數(shù)去實(shí)例化一個(gè)對(duì)象時(shí),js解析又是怎樣呢?比如:

var angela = new Person();

實(shí)例化出來的對(duì)象,js解析也分為下面三步:

  1. 新建一個(gè)對(duì)象并賦值給變量angela:var angela = {};
  2. 把這個(gè)對(duì)象的[[Prototype]]屬性指向函數(shù)Person的原型對(duì)象:angela.[[Prototype]] = Person.prototype
  3. 調(diào)用函數(shù)Person,同時(shí)把this指向剛創(chuàng)建的對(duì)象angela,對(duì)這個(gè)對(duì)象進(jìn)行初始化:Person.apply(angela,arguments)
    如圖可表示為

    總結(jié):構(gòu)造函數(shù)、原型和實(shí)例之間的關(guān)系,每個(gè)構(gòu)造函數(shù)包含一個(gè)指向原型對(duì)象的指針prototype,原型對(duì)象都包含一個(gè)指向構(gòu)造函數(shù)的指針constructor,而實(shí)例都包含一個(gè)指向原型對(duì)象的內(nèi)部指針_proto_(有的地方稱為[[prototype]])。

重寫prototype對(duì)象

在上面兩個(gè)例子的基礎(chǔ)上,再進(jìn)行如下操作

Person.prototype = {
    name: "bruce",
    age: 23
};
  • 上面使用的語法將會(huì)完全重寫默認(rèn)的prototype對(duì)象,其會(huì)直接導(dǎo)致Person的prototype對(duì)象里面沒有了constructor屬性,constructor屬性只能從Person.prototype.proto.constructor繼承過來(Person.prototype的原型對(duì)象為Object原型對(duì)象),即constructor將指向Object構(gòu)造函數(shù)
  • 把原型對(duì)象修改為另外一個(gè)對(duì)象就等于,切斷了構(gòu)造函數(shù)Person和最初的原型對(duì)象之間的聯(lián)系。
    但是實(shí)例angela和最初的原型對(duì)象之間的聯(lián)系不變。
    注:實(shí)例中的_proto_指針僅指向原型,而不指向構(gòu)造函數(shù)。

搜索一個(gè)屬性

  • 每當(dāng)代碼讀取某個(gè)對(duì)象的屬性時(shí),首先會(huì)在對(duì)象實(shí)例本身中搜索,如果在實(shí)例中找到了該屬性,則返回該屬性。如果沒有找到,則繼續(xù)在_proto_指向的原型對(duì)象中搜索,如果找到了該屬性,則返回該屬性。如此繼續(xù)下去。
    也就是說當(dāng)為對(duì)象實(shí)例添加了一個(gè)屬性時(shí),這個(gè)屬性就會(huì)屏蔽原型對(duì)象中保存的同名屬性。
  • hasOwnProperty方法可以檢測(cè)一個(gè)屬性是存在于實(shí)例中,還是存在于原型中,只有給定屬性存在于實(shí)例中,才會(huì)返回true。

prototye屬性

  • JS中萬物皆對(duì)象,通常來說,javascript中的對(duì)象就是一個(gè)指向原型對(duì)象的指針和一個(gè)自身的屬性列表。
    prototype屬性本質(zhì)上它就是一個(gè)普通的指針,其之所以特別,是因?yàn)閖avascript時(shí)讀取屬性時(shí)的遍歷機(jī)制決定的
    只有構(gòu)造函數(shù)才具有prototype屬性,且只有以下對(duì)象才是構(gòu)造函數(shù):
    Object、Function、Array、Date、String
    javascript創(chuàng)建對(duì)象時(shí)采用了寫時(shí)復(fù)制的理念,當(dāng)調(diào)用構(gòu)造函數(shù)創(chuàng)建一個(gè)實(shí)例后,該實(shí)例內(nèi)部將包含一個(gè)指針(內(nèi)部屬性,下圖中暫時(shí)稱為inobj),指向構(gòu)造函數(shù)的原型對(duì)象。
    調(diào)用構(gòu)造函數(shù)創(chuàng)建一個(gè)實(shí)例
  • 而普通對(duì)象是沒有prototype屬性的,在chrome上對(duì)象有一個(gè)_proto_屬性指向?qū)ο蟮脑?,但?br> 在其他瀏覽器上這個(gè)屬性對(duì)外完全不可見,要取得普通對(duì)象的原型對(duì)象,可以調(diào)用Object.getPrototypeOf(instance),便可取得實(shí)例instance的原型。

總結(jié):JS中的每個(gè)對(duì)象都包含一個(gè)指向其原型對(duì)象的指針,這個(gè)指針在構(gòu)造函數(shù)是prototype屬性,可以直接訪問;在普通對(duì)象中是_proto_屬性,不可以直接訪問。

原型鏈

  • 讓一個(gè)函數(shù)的原型對(duì)象的_proto_指針指向一個(gè)實(shí)例,而這個(gè)實(shí)例的_proto_指針又指向另外一個(gè)實(shí)例,如此層層遞進(jìn),就構(gòu)成了實(shí)例與原型之間的一條鏈條,這就是所謂原型鏈的概念。
  • 所有引用類型對(duì)象的_proto_指針默認(rèn)都指向Object,所有函數(shù)的默認(rèn)原型的_proto_指針都指向Object。
  • 原型鏈?zhǔn)荍S實(shí)現(xiàn)繼承的主要方法。
  • 如果實(shí)例A的_proto_指針指向?qū)嵗鼴的原型對(duì)象,可以直接指向,也可以間接指向(即A->C->B),則稱A是B的實(shí)例
  • instanceof操作符
A instanceof B
//如果B的原型對(duì)象出現(xiàn)在A的原型鏈中,則返回true
  • 作用域鏈和原型鏈
    作用域鏈用來查找對(duì)象
    原型鏈用來查找對(duì)象的屬性

參考資料

  1. 公司一位大牛關(guān)于prototype的總結(jié)
  2. 理解js中的原型繼承
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容