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;當改變其中一個實例對象的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__
屬性,當調(diào)用構(gòu)造函數(shù)創(chuàng)建一個新的實例之后,__proto__
就指向了構(gòu)造函數(shù)的原型對象,即構(gòu)造函數(shù)的prototype
屬性。
下圖所示:當定義person1是一個數(shù)組時,person1自帶一個__proto__
屬性。
下圖所示:當定義Person構(gòu)造函數(shù)時,Person自帶一個prototype
屬性。prototype
屬性自帶一個constructor
屬性,至于其他的屬性,都是從Object繼承而來的。
下圖所示:當
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屬性指向的原型對象中有沒有,如果沒有的話,就去查它的原型的原型中有沒有,一直到原型鏈的最頂端為止。