本文實例分析了Javascript原型鏈的原理。分享給大家參考,具體如下:
一、JavaScript原型鏈
ECMAScript中描述了原型鏈的概念,並將原型鍊作為實現繼承的主要方法。其基本思想是利用原型讓一個引用型別繼承另一個引用型別的屬性和方法。在JavaScript中,用 __proto__ 屬性來表示一個物件的原型鏈。當尋找物件的屬性時,JavaScript 會向上遍歷原型鏈,直到找到給定名稱的屬性為止!
例如現在有以下的程式碼:
擴充Object類,加入Clone和Extend方法
/*扩展Object类,添加Clone,JS实现克隆的方法*/ 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; } /*扩展Object类,添加Extend方法来实现JS继承, 目标对象将拥有源对象的所有属性和方法*/ 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類別
/*定义一个Person类*/ function Person(_name,_age){ this.name = _name; this.age = _age; }
在JavaScript中,Object類別是所有類別的父類,所以Person類別從Object類別繼承,繼承了Object類別的所有public屬性和public方法,包括Object類別新加入的Clone和Extend方法
可以用如下的程式碼證明,Person類別確實是繼承了Object類別
document.write("<pre class="brush:php;toolbar:false">"); var p = new Person("孤傲苍狼",24);//创建一个人,名字是孤傲苍狼 var cloneP = p.Clone();//p调用在Object类中定义的Clone方法来克隆自己,如果能得到一个cloneP,那就证明了Person类确实是继承了Object类,所以就拥有了Clone document.writeln("p是使用Person类以构造函数的方式创建出来的对象,p.name = "+p.name+",p.age = "+p.age); document.writeln("cloneP是p调用Clone方法克隆出来的对象,cloneP.name = "+cloneP.name+",cloneP.age = "+cloneP.age); document.writeln("cloneP对象和p对象是两个相互独立的对象,这两个对象的内存地址肯定是不相等,p == cloneP的结果是:"+(p == cloneP)); cloneP.name="白虎神皇";//修改cloneP的名字 document.writeln("cloneP的name被修改了,cloneP.name = "+cloneP.name); document.writeln("cloneP的name修改了,但是不影响到p,p.name = "+p.name); document.write("");
運行結果:
那麼Person類別透過神馬來繼承Object類別的呢,是使用原型(prototye)的方式繼承的:
/*定义一个Person类*/ function Person(_name,_age){ this.name = _name; this.age = _age; } Person.prototype = new Object();//让Person类继承Object类
由於JavaScript規定,任何類別都繼承自Object類,所以"Person.prototype = new Object();//讓Person類別繼承Object類別"即使我們不寫,我猜JavaScript引擎也會自動幫我們加上這句話,或是使用"Person.prototype = Object.prototype;"這種方式,讓Person類別去繼承Object類別。 "Person.prototype = new Object();",其實這樣就相當於Object物件是Person的原型,這樣就相當於了把Object物件的屬性和方法複製到了Person上了。
二、new運算子是如何運作的
我們先來看看這樣一段程式碼:
很簡單的一段程式碼,我們來看看這個new究竟做了什麼?我們可以把new的流程拆分成以下三個步驟:
1.var p={}; 初始化一個物件p。
2. p.__proto__=Person.prototype;,將物件p的 __proto__ 屬性設為 Person.prototype
3.Person.call(p,"孤傲蒼狼",24);呼叫建構子Person來初始化p。
關鍵在於第二步,我們來證明:
var p = new Person("孤傲苍狼",24);//创建一个人,名字是孤傲苍狼 alert("p.__proto__ === Person.prototype的结果是:"+(p.__proto__ === Person.prototype));
在火狐下的運作結果是:
這段程式碼會回傳true。說明我們步驟2的正確。
注意:__proto__這個屬性只有在firefox或chrome瀏覽器中才是公開允許存取的,因此,其他基於IE核心的瀏覽器是不會回傳true的。
那麼__proto__是什麼?在這裡簡單地說下來。每個物件都會在其內部初始化一個屬性,就是__proto__,當我們存取一個物件的屬性時,如果這個物件內部不存在這個屬性,那麼他就會去__proto__裡找這個屬性,這個__proto__又會有自己的__proto__,於是就這樣一直找下去,也就是我們平常所說的原型鏈的概念。
依照標準,__proto__是不對外公開的,也就是說是個私有屬性,在IE下是無法存取__proto__屬性的,但是Firefox的引擎將他暴露了出來成為了一個公有的屬性,我們可以對外存取和設定。
好,概念說清楚了,讓我們來看看下面這些程式碼:
<script type="text/javascript"> var Person = function () { }; Person.prototype.Say = function () { alert("Person say"); } var p = new Person(); p.Say(); </script>
這段程式碼很簡單,我們看下為什麼p可以存取Person的Say。
首先
可以得出
那麼當我們呼叫p.Say()時,首先p中沒有Say這個屬性, 於是,他就需要到他的__proto__中去找,也就是Person.prototype,而我們在上面定義了
Person.prototype.Say=function(){ alert("Person say"); };
于是,就找到了这个方法。
接下来,让我们看个更复杂的。
<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();//让程序员类从人这个类继承 Programmer.prototype.WriteCode = function () { alert("programmer writes code"); }; Programmer.prototype.Salary = 500; var p = new Programmer(); p.Say(); p.WriteCode(); alert(p.Salary); </script>
我们来做这样的推导:
可以得出
而在上面我们指定了
我们来这样拆分,
var p1=new Person(); Programmer.prototype=p1;
那么:
p1.__proto__=Person.prototype; Programmer.prototype.__proto__=Person.prototype;
由根据上面得到
可以得到:
好,算清楚了之后我们来看上面的结果,p.Say()。由于p没有Say这个属性,于是去 p.__proto__,也就是Programmer.prototype,也就是p1中去找,由于p1中也没有Say,那就去 p.__proto__.__proto__,也就是Person.prototype中去找,于是就找到了Say方法。这也就是原型链的实现原理。
以下代码展示了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对象的Say方法
<script type="text/javascript"> /*查找obj对象的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();//让程序员类从人这个类继承,写法一 Programmer.prototype = Person.prototype;//让程序员类从人这个类继承,写法二 Programmer.prototype.WriteCode = function () { alert("programmer writes code"); }; Programmer.prototype.Salary = 500; var p = new Programmer(); var SayFn = getProperty(p,"Say");//查找p对象的Say方法 SayFn.call(p);//调用找到的Say方法 </script>
在火狐下的运行结果:
其实prototype只是一个假象,他在实现原型链中只是起到了一个辅助作用,换句话说,他只是在new的时候有着一定的价值,而原型链的本质,其实在于__proto__。
希望本文所述对大家JavaScript程序设计有所帮助。