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)建一個新的實(shí)例對象的時候刹淌,可以使用new
操作符。
var person1 = new Person(12);
var person2 = new Person(13);
在構(gòu)建的兩個實(shí)例對象的時候讥耗,person1和person2有一個共有屬性name;當(dāng)改變其中一個實(shí)例對象的name的時候,另一個不會發(fā)生改變疹启。
person1.name = "jing";
console.log(person1.name); //jing
console.log(person2.name); //meng
每個實(shí)例對象都有自己的屬性和方法的副本古程,改變其中的一個并不會影響另一個,這就造成了資源和空間的浪費(fèi)喊崖,也無法實(shí)現(xiàn)數(shù)據(jù)的共享挣磨。
為了解決上面的問題,作者Brendan Eich決定使用構(gòu)造函數(shù)設(shè)置一個prototype
屬性茁裙,可以讓所有對象實(shí)例共享它所包含的屬性和方法廊宪。
上面的例子可以寫成:
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
這個屬性傅寡,只要其中一個改變扭倾,就會同時影響兩個實(shí)例對象模聋。
原型是什么
只要創(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)建一個新的實(shí)例之后,__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
一個實(shí)例對象person1時补疑,_proto_
就指向了構(gòu)造函數(shù)Person的原型對象厢钧,即構(gòu)造函數(shù)的prototype
屬性∮┎剩可以對比一個person1的_proto_
屬性在實(shí)例化之前和之后的變化肆汹。
下圖是展示了Person構(gòu)造函數(shù)浪册、Person的原型屬性以及Person現(xiàn)有的兩個實(shí)例之間的關(guān)系厚者。
new運(yùn)算符的工作原理
new 運(yùn)算符接受一個函數(shù)F
及其參數(shù):new F(arguments...)
梧却。這一過程分為三步:
創(chuàng)建類的實(shí)例祖秒。這步是把一個空的對象的__proto__
屬性設(shè)置為 F.prototype
抬纸。
初始化實(shí)例咙俩。函數(shù)F
被傳入?yún)?shù)并調(diào)用,關(guān)鍵字this
被設(shè)定為該實(shí)例湿故。
返回實(shí)例阿趁。
現(xiàn)在我們知道了new
是怎么工作的,我們可以用JS代碼實(shí)現(xiàn)一下:
function New (f) {
var n = { '__proto__': f.prototype }; /*第一步*/
return function () {
f.apply(n, arguments); /*第二步*/
return n; /*第三步*/
};
}
hasOwnProperty函數(shù)
hasOwnProperty
用來判斷該屬性屬于實(shí)例自定義的屬性而不是原型鏈上的屬性晓锻。
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繼承機(jī)制的設(shè)計(jì)思想
JavaScript的原型機(jī)制