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屬性。如圖:

拿上面的例子來說,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__屬性。

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

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

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

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屬性指向的原型對象中有沒有,如果沒有的話,就去查它的原型的原型中有沒有,一直到原型鏈的最頂端為止。