理解JavaScript原型

JavaScript的原型是JavaScript中的重要一環(huán),根據(jù)網(wǎng)上的一些資料和自己的理解,對原型做一個解釋。

prototype屬性的引入

對象可以使用new操作符后跟一個構(gòu)造函數(shù)來創(chuàng)建的。構(gòu)造函數(shù)如下:

function Person(age) {
  this.name = "meng";
  this.age = age;
}

構(gòu)造函數(shù)始終都應(yīng)該以一個大寫字母開頭,而非構(gòu)造函數(shù)則應(yīng)該以一個小寫字母開頭。要創(chuàng)建一個新的實例對象的時候,可以使用new操作符。

var person1 = new Person(12);
var person2 = new Person(13);

在構(gòu)建的兩個實例對象的時候,person1和person2有一個共有屬性name;當(dāng)改變其中一個實例對象的name的時候,另一個不會發(fā)生改變。

person1.name = "jing";
console.log(person1.name);  //jing
console.log(person2.name);  //meng

每個實例對象都有自己的屬性和方法的副本,改變其中的一個并不會影響另一個,這就造成了資源和空間的浪費,也無法實現(xiàn)數(shù)據(jù)的共享。

為了解決上面的問題,作者Brendan Eich決定使用構(gòu)造函數(shù)設(shè)置一個prototype屬性,可以讓所有對象實例共享它所包含的屬性和方法。

上面的例子可以寫成:

function Person(age) {
  this.age = age;
}
Person.prototype.name = "meng";

var person1 = new Person(12);
var person2 = new Person(13);
Person.prototype.name = "jing";
console.log(person1.name);  //jing
console.log(person2.name);  //jing

這時person1和person2就共享了Person.prototype.name這個屬性,只要其中一個改變,就會同時影響兩個實例對象。

原型是什么

只要創(chuàng)建了一個新函數(shù),就會根據(jù)一組特定的規(guī)則為該函數(shù)創(chuàng)建一個prototype屬性,這個屬性指向函數(shù)的原型對象。所有原型對象都會獲得一個指向prototype對象的constructor屬性。如圖:

14805248662081.jpg

拿上面的例子來說,Person.prototype.constructor又指向了Person。

proto、prototype和constructor

每一個生成的對象都有一個__proto__屬性,當(dāng)調(diào)用構(gòu)造函數(shù)創(chuàng)建一個新的實例之后,__proto__就指向了構(gòu)造函數(shù)的原型對象,即構(gòu)造函數(shù)的prototype屬性。

下圖所示:當(dāng)定義person1是一個數(shù)組時,person1自帶一個__proto__屬性。

14805256492639.jpg

下圖所示:當(dāng)定義Person構(gòu)造函數(shù)時,Person自帶一個prototype屬性。prototype屬性自帶一個constructor屬性,至于其他的屬性,都是從Object繼承而來的。

14805260237657.jpg

下圖所示:當(dāng)new一個實例對象person1時,_proto_就指向了構(gòu)造函數(shù)Person的原型對象,即構(gòu)造函數(shù)的prototype屬性??梢詫Ρ纫粋€person1的_proto_屬性在實例化之前和之后的變化。

14805259038416.jpg

下圖是展示了Person構(gòu)造函數(shù)、Person的原型屬性以及Person現(xiàn)有的兩個實例之間的關(guān)系。


14805245325304.jpg

new運算符的工作原理

new 運算符接受一個函數(shù)F及其參數(shù):new F(arguments...)。這一過程分為三步:

創(chuàng)建類的實例。這步是把一個空的對象的__proto__屬性設(shè)置為 F.prototype
初始化實例。函數(shù)F被傳入?yún)?shù)并調(diào)用,關(guān)鍵字this被設(shè)定為該實例。
返回實例。
現(xiàn)在我們知道了new是怎么工作的,我們可以用JS代碼實現(xiàn)一下:

function New (f) {
    var n = { '__proto__': f.prototype }; /*第一步*/
    return function () {
        f.apply(n, arguments);            /*第二步*/
        return n;                         /*第三步*/
    };
}

hasOwnProperty函數(shù)

hasOwnProperty用來判斷該屬性屬于實例自定義的屬性而不是原型鏈上的屬性。

function Person(age) {
  this.age = age;
}
Person.prototype.name = "meng";

var person1 = new Person(12);
person1.hasOwnProperty('age');  //true
person1.hasOwnProperty('name'); //false

JavaScript引擎如何來查找屬性

以下代碼展示了JS引擎如何查找屬性:

function getProperty(obj, prop) {
    if (obj.hasOwnProperty(prop))
        return obj[prop]

    else if (obj.__proto__ !== null)
        return getProperty(obj.__proto__, prop)

    else
        return undefined
}

通過上面的代碼可以看出來,js先查找自身有沒有該屬性,如果沒有的話,就查找proto屬性指向的原型對象中有沒有,如果沒有的話,就去查它的原型的原型中有沒有,一直到原型鏈的最頂端為止。

參考:
阮一峰:Javascript繼承機制的設(shè)計思想
JavaScript的原型機制

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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