Javascript原型鏈的原理詳解
本文實(shí)例分析了Javascript原型鏈的原理。分享給大家供大家參考,具體如下:
一、JavaScript原型鏈
ECMAScript中描述了原型鏈的概念,并將原型鏈作為實(shí)現(xiàn)繼承的主要方法。其基本思想是利用原型讓一個(gè)引用類型繼承另一個(gè)引用類型的屬性和方法。在JavaScript中,用 __proto__ 屬性來(lái)表示一個(gè)對(duì)象的原型鏈。當(dāng)查找一個(gè)對(duì)象的屬性時(shí),JavaScript 會(huì)向上遍歷原型鏈,直到找到給定名稱的屬性為止!
比如現(xiàn)在有如下的代碼:
擴(kuò)展Object類,添加Clone和Extend方法
/*擴(kuò)展Object類,添加Clone,JS實(shí)現(xiàn)克隆的方法*/
Object.prototype.Clone = function(){
var objClone;
if (this.constructor == Object){
objClone = new this.constructor();
}else{
objClone = new this.constructor(this.valueOf());
}
for(var key in this){
if ( objClone[key] != this[key] ){
if ( typeof(this[key]) == 'object' ){
objClone[key] = this[key].Clone();
}else{
objClone[key] = this[key];
}
}
}
objClone.toString = this.toString;
objClone.valueOf = this.valueOf;
return objClone;
}
/*擴(kuò)展Object類,添加Extend方法來(lái)實(shí)現(xiàn)JS繼承, 目標(biāo)對(duì)象將擁有源對(duì)象的所有屬性和方法*/
Object.prototype.Extend = function (objDestination, objSource) {
for (var key in objSource) {
if (objSource.hasOwnProperty(key) && objDestination[key] === undefined) {
objDestination[key] = objSource[key];
}
}
return objDestination;
}
定義Person類
/*定義一個(gè)Person類*/
function Person(_name,_age){
this.name = _name;
this.age = _age;
}
在JavaScript中,Object類是所有類的父類,所以Person類從Object類繼承,繼承了Object類的所有public屬性和public方法,包括Object類新添加的Clone和Extend方法
可以用如下的代碼證明,Person類確實(shí)是繼承了Object類
document.write("<pre>");
var p = new Person("孤傲蒼狼",24);//創(chuàng)建一個(gè)人,名字是孤傲蒼狼
var cloneP = p.Clone();//p調(diào)用在Object類中定義的Clone方法來(lái)克隆自己,如果能得到一個(gè)cloneP,那就證明了Person類確實(shí)是繼承了Object類,所以就擁有了Clone
document.writeln("p是使用Person類以構(gòu)造函數(shù)的方式創(chuàng)建出來(lái)的對(duì)象,p.name = "+p.name+",p.age = "+p.age);
document.writeln("cloneP是p調(diào)用Clone方法克隆出來(lái)的對(duì)象,cloneP.name = "+cloneP.name+",cloneP.age = "+cloneP.age);
document.writeln("cloneP對(duì)象和p對(duì)象是兩個(gè)相互獨(dú)立的對(duì)象,這兩個(gè)對(duì)象的內(nèi)存地址肯定是不相等,p == cloneP的結(jié)果是:"+(p == cloneP));
cloneP.name="白虎神皇";//修改cloneP的名字
document.writeln("cloneP的name被修改了,cloneP.name = "+cloneP.name);
document.writeln("cloneP的name修改了,但是不影響到p,p.name = "+p.name);
document.write("</pre>");
運(yùn)行結(jié)果:

那么Person類通過(guò)神馬方式來(lái)繼承Object類的呢,是使用原型(prototye)的方式繼承的:
/*定義一個(gè)Person類*/
function Person(_name,_age){
this.name = _name;
this.age = _age;
}
Person.prototype = new Object();//讓Person類繼承Object類
由于JavaScript規(guī)定,任何類都繼承自O(shè)bject類,所以"Person.prototype = new Object();//讓Person類繼承Object類"即使我們不寫,我猜想JavaScript引擎也會(huì)自動(dòng)幫我們加上這句話,或者是使用"Person.prototype = Object.prototype;"這種方式,讓Person類去繼承Object類。"Person.prototype = new Object();",其實(shí)這樣就相當(dāng)于Object對(duì)象是Person的一個(gè)原型,這樣就相當(dāng)于了把Object對(duì)象的屬性和方法復(fù)制到了Person上了。
二、new運(yùn)算符是如何工作的
我們先看看這樣一段代碼:
很簡(jiǎn)單的一段代碼,我們來(lái)看看這個(gè)new究竟做了什么?我們可以把new的過(guò)程拆分成以下三步:
1.var p={}; 初始化一個(gè)對(duì)象p。
2. p.__proto__=Person.prototype;,將對(duì)象p的 __proto__ 屬性設(shè)置為 Person.prototype
3.Person.call(p,"孤傲蒼狼",24);調(diào)用構(gòu)造函數(shù)Person來(lái)初始化p。
關(guān)鍵在于第二步,我們來(lái)證明一下:
var p = new Person("孤傲蒼狼",24);//創(chuàng)建一個(gè)人,名字是孤傲蒼狼
alert("p.__proto__ === Person.prototype的結(jié)果是:"+(p.__proto__ === Person.prototype));
在火狐下的運(yùn)行結(jié)果是:

這段代碼會(huì)返回true。說(shuō)明我們步驟2的正確。
注意:__proto__這個(gè)屬性只有在firefox或者chrome瀏覽器中才是公開(kāi)允許訪問(wèn)的,因此,其他基于IE內(nèi)核的瀏覽器是不會(huì)返回true的。
那么__proto__是什么?在這里簡(jiǎn)單地說(shuō)下。每個(gè)對(duì)象都會(huì)在其內(nèi)部初始化一個(gè)屬性,就是 __proto__,當(dāng)我們?cè)L問(wèn)一個(gè)對(duì)象的屬性時(shí),如果這個(gè)對(duì)象內(nèi)部不存在這個(gè)屬性,那么他就會(huì)去__proto__里找這個(gè)屬性,這個(gè) __proto__又會(huì)有自己的__proto__,于是就這樣一直找下去,也就是我們平時(shí)所說(shuō)的原型鏈的概念。
按照標(biāo)準(zhǔn),__proto__是不對(duì)外公開(kāi)的,也就是說(shuō)是個(gè)私有屬性,在IE下是無(wú)法訪問(wèn)__proto__屬性的,但是Firefox的引擎將他暴露了出來(lái)成為了一個(gè)公有的屬性,我們可以對(duì)外訪問(wèn)和設(shè)置。
好,概念說(shuō)清了,讓我們看一下下面這些代碼:
<script type="text/javascript">
var Person = function () { };
Person.prototype.Say = function () {
alert("Person say");
}
var p = new Person();
p.Say();
</script>
這段代碼很簡(jiǎn)單,我們看下為什么p可以訪問(wèn)Person的Say。
首先
可以得出
那么當(dāng)我們調(diào)用p.Say()時(shí),首先p中沒(méi)有Say這個(gè)屬性, 于是,他就需要到他的__proto__中去找,也就是Person.prototype,而我們?cè)谏厦娑x了
Person.prototype.Say=function(){
alert("Person say");
};
于是,就找到了這個(gè)方法。
接下來(lái),讓我們看個(gè)更復(fù)雜的。
<script type="text/javascript">
var Person = function () { };
Person.prototype.Say = function () {
alert("Person say");
}
Person.prototype.Salary = 50000;
var Programmer = function () { };
Programmer.prototype = new Person();//讓程序員類從人這個(gè)類繼承
Programmer.prototype.WriteCode = function () {
alert("programmer writes code");
};
Programmer.prototype.Salary = 500;
var p = new Programmer();
p.Say();
p.WriteCode();
alert(p.Salary);
</script>
我們來(lái)做這樣的推導(dǎo):
可以得出
而在上面我們指定了
我們來(lái)這樣拆分,
var p1=new Person(); Programmer.prototype=p1;
那么:
p1.__proto__=Person.prototype; Programmer.prototype.__proto__=Person.prototype;
由根據(jù)上面得到
可以得到:
好,算清楚了之后我們來(lái)看上面的結(jié)果,p.Say()。由于p沒(méi)有Say這個(gè)屬性,于是去 p.__proto__,也就是Programmer.prototype,也就是p1中去找,由于p1中也沒(méi)有Say,那就去 p.__proto__.__proto__,也就是Person.prototype中去找,于是就找到了Say方法。這也就是原型鏈的實(shí)現(xiàn)原理。
以下代碼展示了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;
}
范例:查找p對(duì)象的Say方法
<script type="text/javascript">
/*查找obj對(duì)象的prop屬性*/
function getProperty(obj, prop) {
if (obj.hasOwnProperty(prop))
return obj[prop];
else if (obj.__proto__ !== null)
return getProperty(obj.__proto__, prop);//遞歸
else
return undefined;
}
var Person = function () { };//定義Person類
Person.prototype.Say = function () {
alert("Person say");
}
Person.prototype.Salary = 50000;
var Programmer = function () { };//定義Programmer類
//Programmer.prototype = new Person();//讓程序員類從人這個(gè)類繼承,寫法一
Programmer.prototype = Person.prototype;//讓程序員類從人這個(gè)類繼承,寫法二
Programmer.prototype.WriteCode = function () {
alert("programmer writes code");
};
Programmer.prototype.Salary = 500;
var p = new Programmer();
var SayFn = getProperty(p,"Say");//查找p對(duì)象的Say方法
SayFn.call(p);//調(diào)用找到的Say方法
</script>
在火狐下的運(yùn)行結(jié)果:

其實(shí)prototype只是一個(gè)假象,他在實(shí)現(xiàn)原型鏈中只是起到了一個(gè)輔助作用,換句話說(shuō),他只是在new的時(shí)候有著一定的價(jià)值,而原型鏈的本質(zhì),其實(shí)在于__proto__。
希望本文所述對(duì)大家JavaScript程序設(shè)計(jì)有所幫助。
相關(guān)文章
Javascript實(shí)現(xiàn)表單檢驗(yàn)
這篇文章主要介紹了Javascript實(shí)現(xiàn)表單檢驗(yàn),以注冊(cè)界面為例,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06
javascript 利用arguments實(shí)現(xiàn)可變長(zhǎng)參數(shù)
在C#中,有可變長(zhǎng)參數(shù)params[],但是在js中,如何實(shí)現(xiàn)這種可變參數(shù)呢?本片文章主要介紹利用arguments,實(shí)現(xiàn)可變長(zhǎng)參數(shù)。有需要的請(qǐng)參考下2016-11-11
在window.setTimeout方法中傳送對(duì)象
setTimeout方法是js中的延時(shí)方法,很多js的bug,只需要使用該方法延時(shí)一下,就會(huì)自動(dòng)解決了,簡(jiǎn)直就是萬(wàn)能藥方,也是我比較喜歡使用的最后手段。2006-12-12
微信小程序?qū)崿F(xiàn)電子簽名并導(dǎo)出圖片
這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)電子簽名,并導(dǎo)出圖片,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05
Bootstrap的popover(彈出框)在append后彈不出(失效)
這篇文章主要介紹了Bootstrap的popover(彈出框)在append后彈不出,失效的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02

