JavaScript中原型和原型鏈詳解
投稿:junjie 字型:[增加 減小] 類型:轉載 時間:2015-02-11 我要評論 這篇文章主要介紹了JavaScript中原型和原型鏈詳解,本文講解了私人變數和函數、靜態變數和函數、執行個體變數和函數、原型和原型鏈的基本概念,需要的朋友可以參考下 <iframe id="iframeu2261530_0" src="http://pos.baidu.com/pcmm?sz=680x200&rdid=2261530&dc=2&di=u2261530&dri=0&dis=0&dai=2&ps=0x0&coa=at%3D3%26rsi0%3D680%26rsi1%3D200%26pat%3D6%26tn%3DbaiduCustNativeAD%26rss1%3D%2523FFFFFF%26conBW%3D1%26adp%3D1%26ptt%3D0%26titFF%3D%2525E5%2525BE%2525AE%2525E8%2525BD%2525AF%2525E9%25259B%252585%2525E9%2525BB%252591%26titFS%3D14%26rss2%3D%2523000000%26titSU%3D0%26ptbg%3D90%26piw%3D0%26pih%3D0%26ptp%3D0&dcb=BAIDU_SSP_define&dtm=HTML_POST&dvi=0.0&dci=-1&dpt=none&tsr=0&tpr=1474878500512&ti=JavaScript%E4%B8%AD%E5%8E%9F%E5%9E%8B%E5%92%8C%E5%8E%9F%E5%9E%8B%E9%93%BE%E8%AF%A6%E8%A7%A3_%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86_%E8%84%9A%E6%9C%AC%E4%B9%8B%E5%AE%B6&ari=2&dbv=2&drs=1&pcs=1347x638&pss=1347x15554&cfv=0&cpl=6&chi=1&cce=true&cec=GBK&tlm=1473124338&rw=638&ltu=http%3A%2F%2Fwww.jb51.net%2Farticle%2F61014.htm&ltr=http%3A%2F%2Fwww.jb51.net%2Farticle%2F55540.htm&ecd=1&psr=1366x768&par=1366x728&pis=-1x-1&ccd=24&cja=false&cmi=8&col=zh-CN&cdo=-1&tcn=1474878501&qn=84a5a8f34388a294&tt=1474878500403.522.524.528" width="680" height="200" align="center,center" vspace="0" hspace="0" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" allowtransparency="true" style="display: block; border-width: 0px; border-style: initial; vertical-align: bottom; margin: 0px;"></iframe>
javascript中的每個對象都有一個內建的屬性prototype,Javascript中對象的prototype屬性的解釋是:返回物件類型原型的引用。意思是是prototype屬性儲存著對另一個JavaScript對象的引用,這個對象作為當前對象的父物件。
複製代碼代碼如下:
A.prototype = new B();
理解prototype不應把它和繼承混淆。A的prototype為B的一個執行個體,可以理解A將B中的方法和屬性全部複製了一遍。A能使用B的方法和屬性。這裡強調的是複製而不是繼承。可以出現這種情況:A的prototype是B的執行個體,同時B的prototype也是A的執行個體。
繼續看下面的分析:
私人變數和函數
在函數內部定義的變數和函數,如果不對外提供介面,外部是無法訪問到的,也就是該函數的私人的變數和函數。
複製代碼代碼如下:
<script type="text/javascript">
function Box(){
var color = "blue";//私人變數
var fn = function() //私人函數
{
}
}
</script>
這樣在函數對象Box外部無法訪問變數color和fn,他們就變成私人的了:
複製代碼代碼如下:
var obj = new Box();
alert(obj.color);//彈出 undefined
alert(obj.fn);//同上
靜態變數和函數
當定義一個函數後通過點號 “.”為其添加的屬性和函數,通過對象本身仍然可以訪問得到,但是其執行個體卻訪問不到,這樣的變數和函數分別被稱為靜態變數和靜態函數。
複製代碼代碼如下:
<script type="text/javascript">
function Obj(){};
Obj.num = 72;//靜態變數
Obj.fn = function() //靜態函數
{
}
alert(Obj.num);//72
alert(typeof Obj.fn)//function
var t = new Obj();
alert(t.name);//undefined
alert(typeof t.fn);//undefined
</script>
執行個體變數和函數
在物件導向編程中除了一些庫函數我們還是希望在對象定義的時候同時定義一些屬性和方法,執行個體化後可以訪問,js也能做到這樣
複製代碼代碼如下:
<script type="text/javascript">
function Box(){
this.a=[]; //執行個體變數
this.fn=function(){ //執行個體方法
}
}
console.log(typeof Box.a); //undefined
console.log(typeof Box.fn); //undefined
var box=new Box();
console.log(typeof box.a); //object
console.log(typeof box.fn); //function
</script>
為執行個體變數和方法添加新的方法和屬性
複製代碼代碼如下:
<script type="text/javascript">
function Box(){
this.a=[]; //執行個體變數
this.fn=function(){ //執行個體方法
}
}
var box1=new Box();
box1.a.push(1);
box1.fn={};
console.log(box1.a); //[1]
console.log(typeof box1.fn); //object
var box2=new Box();
console.log(box2.a); //[]
console.log(typeof box2.fn); //function
</script>
在box1中修改了a和fn,而在box2中沒有改變,由於數組和函數都是對象,是參考型別,這就說明box1中的屬性和方法與box2中的屬性與方法雖然同名但卻不是一個引用,而是對Box對象定義的屬性和方法的一個複製。
這個對屬性來說沒有什麼問題,但是對於方法來說問題就很大了,因為方法都是在做完全一樣的功能,但是卻又兩份複製,如果一個函數對象有上千和執行個體方法,那麼它的每個執行個體都要保持一份上千個方法的複製,這顯然是不科學的,這可腫麼辦呢,prototype應運而生。
基本概念
我們建立的每個函數都有一個prototype屬性,這個屬性是一個指標,指向一個對象,這個對象的用途是包含可以由特定類型的所有執行個體共用的屬性和方法。那麼,prototype就是通過調用建構函式而建立的那個對象執行個體的原型對象。
使用原型的好處是可以讓對象執行個體共用它所包含的屬性和方法。也就是說,不必在建構函式中添加定義對象資訊,而是可以直接將這些資訊添加到原型中。使用建構函式的主要問題就是每個方法都要在每個執行個體中建立一遍。
在JavaScript中,一共有兩種類型的值,原始值和對象值。每個對象都有一個內部屬性 prototype ,我們通常稱之為原型。原型的值可以是一個對象,也可以是null。如果它的值是一個對象,則這個對象也一定有自己的原型。這樣就形成了一條線性鏈,我們稱之為原型鏈。
含義
函數可以用來作為建構函式來使用。另外只有函數才有prototype屬性並且可以訪問到,但是對象執行個體不具有該屬性,只有一個內部的不可訪問的__proto__屬性。__proto__是對象中一個指向相關原型的神秘連結。按照標準,__proto__是不對外公開的,也就是說是個私人屬性,但是Firefox的引擎將他暴露了出來成為了一個共有的屬性,我們可以對外訪問和設定。
複製代碼代碼如下:
<script type="text/javascript">
var Browser = function(){};
Browser.prototype.run = function(){
alert("I'm Gecko,a kernel of firefox");
}
var Bro = new Browser();
Bro.run();
</script>
當我們調用Bro.run()方法時,由於Bro中沒有這個方法,所以,他就會去他的__proto__中去找,也就是Browser.prototype,所以最終執行了該run()方法。(在這裡,函數首字母大寫的都代表建構函式,以用來區分普通函數)
當調用建構函式建立一個執行個體的時候,執行個體內部將包含一個內部指標(__proto__)指向建構函式的prototype,這個串連存在於執行個體和建構函式的prototype之間,而不是執行個體與建構函式之間。
複製代碼代碼如下:
<script type="text/javascript">
function Person(name){ //建構函式
this.name=name;
}
Person.prototype.printName=function() //原型對象
{
alert(this.name);
}
var person1=new Person('Byron');//執行個體化對象
console.log(person1.__proto__);//Person
console.log(person1.constructor);//自己試試看會是什麼吧
console.log(Person.prototype);//指向原型對象Person
var person2=new Person('Frank');
</script>
Person的執行個體person1中包含了name屬性,同時自動產生一個__proto__屬性,該屬性指向Person的prototype,可以訪問到prototype內定義的printName方法,大概就是這個樣子的:
搬家上58同城先行賠付有保障. 【點擊進入】 提供各類車型價格統一專業單位/住宅搬家 24小時就近服務搬家上58讓您省心又省力. 查 看
每個JavaScript函數都有prototype屬性,這個屬性引用了一個對象,這個對象就是原型對象。原型對象初始化的時候是空的,我們可以在裡面自訂任何屬性和方法,這些方法和屬性都將被該建構函式所建立的對象繼承。
那麼,現在問題來了。建構函式、執行個體和原型對象三者之間有什麼關係呢。
建構函式、執行個體和原型對象的區別
執行個體就是通過建構函式建立的。執行個體一創造出來就具有constructor屬性(指向建構函式)和__proto__屬性(指向原型對象),
建構函式中有一個prototype屬性,這個屬性是一個指標,指向它的原型對象。
原型對象內部也有一個指標(constructor屬性)指向建構函式:Person.prototype.constructor = Person;
執行個體可以訪問原型對象上定義的屬性和方法。
在這裡person1和person2就是執行個體,prototype是他們的原型對象。
再舉個栗子:
複製代碼代碼如下:
<script type="text/javascript">
function Animal(name) //積累建構函式
{
this.name = name;//設定對象屬性
}
Animal.prototype.behavior = function() //給基類建構函式的prototype添加behavior方法
{
alert("this is a "+this.name);
}
var Dog = new Animal("dog");//建立Dog對象
var Cat = new Animal("cat");//建立Cat對象
Dog.behavior();//通過Dog對象直接調用behavior方法
Cat.behavior();//output "this is a cat"
alert(Dog.behavior==Cat.behavior);//output true;
</script>
可以從程式運行結果看出,建構函式的prototype上定義的方法確實可以通過對象直接調用到,而且代碼是共用的。(可以試一下將Animal.prototype.behavior 中的prototype屬性去掉,看看還能不能運行。)在這裡,prototype屬性指向Animal對象。
數組對象執行個體
再看個數組對象的執行個體。當我們建立出array1這個對象的時候,array1實際在Javascript引擎中的物件模型如下:
複製代碼代碼如下:
var array1 = [1,2,3];