JS 原型鏈
javascript所有物件都繼承自Object類。以下是Object類的一些屬性。原型物件
的一些屬性。
1、constructor屬性
從javascript1.1開始,每個物件都有這個屬性,它指向用來初始化改物件的構造
函式
< SCRIPT. LANGUAGE = " JavaScript. " >
<!--
function Person() {}
var o = new Person();
alert( typeof (Person.constructor));
alert(o.constructor);
alert(Person.constructor);
alert(Function.constructor);
alert(Object.constructor)
alert( new Date().constructor);
alert(Date.constructor);
function Man() {
}
Man.prototype = new Person();
alert(Man.constructor);
o = new Man();
alert(o.constructor)
Man.prototype.constructor = Man;
alert(o.constructor)
// -->
SCRIPT. >
如以上程式碼,可以知道
a、constructor的型別是函式;
b、javascript內部實現了很多函式,如Object,Date都是函式由Function得到的
。
c、用原型物件實現的繼承中,也要設定子類的constructor。如果你的程式中用
到了constructor,可能會出錯。
這是關於原型鏈的一系列的現象與原理的解釋以及例子
【轉載請註明出處與地址】
分成4個部分闡述: 1.如何建立一個物件
2.使用原型鏈prototype實現物件的繼承.
3.原型鏈上屬性和方法的聯絡與規則
4.深入剖析原型鏈。
一、如何建立一個物件:
1.使用關鍵字new建立。
var bj=new Object;
或者
function c()
{}
var bj=new c();
2.使用花括號建立。
var bj={};
我們知道,function是定義一個函式,裡面的var 都是區域性變數,那麼如何宣告一個物件的屬性和方法呢。祕密就在於this關鍵字。在函式內部使用this關鍵字,就相當於是物件的屬性和方法的宣告這個變數是存在的。具體的例子如下:
function a()
{
this.a=1;
this.get=function()
{
return this.a;
}
}
那麼只要使用用var bj =new a();即建立一個物件的例項,裡面就包括function裡面的this屬性和方法的就會直接拷貝到物件變數obj上。直接使用a.a的方法即可呼叫相應的屬性和方法。
但是注意,除非例項化function a(),否則無法呼叫裡面的this關鍵字指向的屬性以及方法。
因為此時this上定義的屬性以及方法類似於C++/JAVA等物件導向宣告的屬性以及方法。因此,這種定義函式的方法也被稱為JS的建構函式。
二、使用原型鏈prototype實現物件的繼承.
所有函式function都有一個預設的屬性prototype,通過用typeof函式測試我們可以看到prototype是一個物件。
alert(typeof(a.prototype));
結果:
那麼這個prototype的屬性到底有什麼用呢~?
既然是一個物件那麼賦值的時候就當然是付給prototype一個例項了。事實上,只要把一個例項賦給prototype這個屬性,那麼這個function就具有這個例項的屬性和方法。相當於我們常說的繼承。請看下面的程式碼:
function a()
{
this.ia=1;
this.get=function()
{
return this.ia;
}
}
function b() //b建構函式
{
this.ic=10;
this.getfatherclass=function()
{
return this.ic;
}
}
b.prototype=new a();//繼承例項。
var bj =new b();//例項化函式b
alert(obj.get());
就是這麼簡單的邏輯,我們就是使用物件b例項化的obj,輸出繼承了父類a的方法get。輸出父類a裡面的屬性ia。大家可以看到,這樣就輕鬆地實現了繼承。
那如果a又繼承另外一個物件x怎麼辦?
答案當然是b會繼承a所繼承的所有東西。而他們的關係就像用prototype屬性連起來一樣。所以我們可以看成也是一個由prototype連線而成的連結串列,人稱原型鏈。
三、原型鏈上屬性和方法的聯絡與規則
首先我們明確一個概念,原型鏈就是一串好像用屬性prototype連線而成的,每個節點具有屬性和方法甚至函式過程的鏈。這種鏈式結構我們無疑可以發現一個很重要的問題----鏈上的節點之間發生屬性或者方法重名怎麼辦?下面我們從讀和寫兩方面來解析這條原型鏈的屬性和方法的使用過程。
讀:
從上面的例子裡,alert(obj.get())輸出的結果是什麼呢,如果執行過一次,我們可以發現輸出的結果是1.
但是我們可以看到,建構函式b定義的時候是沒有get的方法的,同時,get方法呼叫的屬性ia也是b中不存在的,那麼我們在擁有prototype繼承的建構函式所例項化的物件是如何呼叫屬性和方法的呢。
結論是:對於呼叫例項化某一建構函式的物件的方法或者屬性的時候,js會在原型鏈 最結尾的點,也就是C++/JAVA上所描述的子類,上述例子中的b函式上,查詢是否有所需要的方法或者屬性,如果找不到的話,就會根據賦值給 prototype的父節點物件,父建構函式上查詢對應的屬性和方法,就這樣按照這種規則查詢出對應名稱的屬性或方法。如果鏈上節點都不存在的話,當然就 會輸出undefined。
如果子節點有該屬性或者方法,同時父節點也有的時候,當然會優先考慮子節點的,這也類似於C++/JAVA上所描述的覆蓋的概念,子類的屬性或者方法覆蓋父類的。
而 在上述例子中,get方法呼叫了一個this.ia,那麼查詢此此元素的時候,也會遵循上訴的原則,先查詢建構函式b上是否有屬性ia,沒有的話再查詢a 上的建構函式上的屬性ia。最後輸出結果1。如果我們在建構函式b上新增一個屬性this.ia=2,那麼結果會如上述結論所說輸出2.
寫:
我 們都知道,new操作會建立一個和建構函式一模一樣的副本,開闢一個空間存放物件。但是我們從上面的操作可以看到,對於函式鏈上的屬性,都可以被各個例項 所訪問,那麼,js在例項化有prototype繼承的建構函式的時候,是把鏈上的節點都例項化一次,還是被所有例項所共享呢?下面我們看下面例子。
function a()
{
this.ia=1;
this.get=function()
{
return this.ia;
}
}
function b() //b建構函式
{
this.ic=10;
this.getfatherclass=function()
{
return this.ic;
}
}
b.prototype=new a();//繼承例項。
var obj1 =new b();//例項化函式b
var obj2 =new b();//例項化函式b
alert(obj1.ia);//輸出1,因為他是父類a中的ia
alert(obj2.ia);//輸出1,因為他是父類a中的ia
obj1.ia=2;
alert(obj1.ia);//輸出2,因為寫操作會在例項物件建立一個副本ia並且賦值2,無修改其原型鏈上的父節點。
alert(obj2.ia);//輸出還是1,因為他是父類a中的ia
b.prototype.ia=3;//修改原型鏈上的屬性ia。
alert(obj1.ia);//輸出2,因為已經在例項上建立了副本,所以優先讀取副本。
alert(obj2.ia);//輸出3,因為子類b中不存在,所以還是查詢父節點(prototype物件上的物件)上的ia,由於父節點上的ia已經被修改成3,所以輸出3.
上面的例子說明了2件事:
1.寫操作並不是直接查詢原型鏈上的屬性,然後更改他,而是直接在例項上建立在一個同名物件。由於例項上已經存在這個屬性,所以下次讀取的時候優先讀取這個已存在的,相當於原型鏈上的原屬性被覆蓋(但是還是存在的)。
2.Prototype鏈上的節點(父類)都是被子類例項化後的物件所共享,也就是隻有唯一的一個物件prototype,因此當我們修改b.prototype.ia=3;/後,obj2輸出的也是3,說明他是被其他沒有建立ia屬性副本的例項所共享的。
這樣我們就清晰了,原型鏈只有一條,並且不會因為子類例項化和把鏈上的節點,屬性與方法都例項化,他將被所有子類的例項化物件所共享。
而這個原型鏈上的父類是準讀不準寫(通過子類例項化物件改變鏈上節點的屬性與方法)。一旦你的例項化物件進行和父類屬性與方法同名的寫操作時,就會在例項化物件上建立對應的副本,同時進行寫操作,此時這個例項也覆蓋了原型鏈上的這個屬性和方法。這種方法很好的保護了原型鏈上節點的共享性,不用擔心不小心被其他例項所修改。這樣大大地節省了記憶體,可以在頻繁的物件建立中,有效地減少了記憶體空間的使用。
當然可以通過函式b上的prototype物件來修改鏈上的屬性與方法,這樣就可以做到一改全改,讓子類共享的屬性發生改變。如上面的 b.prototype.ia=3;操作。
這種做法也衍生出一種叫做延時繫結的思想,在使用的時侯才進行屬性和方法的繫結,這種行為是可以通過原型鏈、 prototype這個屬性實現的。這樣可以很方便地應用到你的程式設計中,但是注意,這種方法還是弊大於利的,因為可以在任何地方進行重繫結。所以程式者也 許不知道在何時進行了繫結,使得程式碼很混亂,同時在大程式碼量的情況下,照成不可預料的結果。因此,延時繫結必須慎用。
四、深入剖析原型鏈。
上面說到,原型鏈是由prototype連在一起的,事實上這種說法是不嚴謹,甚至可以說是錯誤的,因為prototype是一個物件,並且例項是沒有prototype這個屬性的,但是,有比prototype更好的,用來表現原型鏈的東西,這是物件例項後的一個私有屬性,ie下是不可訪問的,但是用firefox是可以檢視的,它才是真正的作為例項在原型鏈上查詢屬性與方法的一個類似指標的屬性。可以這麼說,prototype是函式所有的,而__proto__是例項化的物件存在的屬性。我們常把這個__proto__指向的原型成為父原型。因為prototype是一個物件的例項,所以原型也有父原型。下面給出一段例子來解釋這個現象。
function a()
{
this.ia=1;
this.get=function()
{
return this.ia;
}
}
function b() //b建構函式
{
this.ic=10;
this.getfatherclass=function()
{
return this.ic;
}
}
b.prototype=new a();//繼承例項。
alert(a instanceof Function);//a是Function的例項;
alert(a.__proto__ === Function.prototype);//a的父原型指向到Function的原型;
alert(b.__proto__ === Function.prototype);//a的父原型指向到Function的原型
var bj =new b();//例項化函式b
alert(typeof(obj.__proto__));//輸出obejct,說明是一個物件
alert(obj instanceof Object);//ture.obj是其中一個物件
alert(obj.__proto__===b.prototype);//ture。因為__prototype指向父原型
alert(obj.__proto__.__proto__===a.prototype);//ture。因為__prototype指向父原型
alert(obj.__proto__.__proto__.__proto__===Object.prototype);//ture。因為__prototype指向父原型
alert(Object.prototype.__proto__);//null。因為Object的原型是所有父原型的頂端,它不再具有父原型;
alert(Function instanceof Object);//Function是Object的例項;
alert(Function.__proto__ === Function.prototype);//Function的父原型指向到Function的原型;
alert(Function.prototype.__proto__ === Object.prototype);//Function的原型的父原型指向到Object的原型
alert(Object.__proto__ === Function.prototype);//Object的父原型指向到Function的原型;
從上面的例子我們再也清晰不過了,例項查詢原型鏈的時候使用的__proto__ 這個私有屬性,通過這個屬性一層一層地遍歷原型鏈,而原型鏈的最終父原型是Object.prototype,它是所有例項的父原型,只要修改這個父原型,那麼所有的例項都將用到修改的屬性。
同時。Function.prototype是所有函式的父原型,而它Function.prototype的父原型也是Object.prototype。到了這裡,我們可以清晰地看到一個例項後面的原型鏈的全貌了。
的一些屬性。
1、constructor屬性
從javascript1.1開始,每個物件都有這個屬性,它指向用來初始化改物件的構造
函式
< SCRIPT. LANGUAGE = " JavaScript. " >
<!--
function Person() {}
var o = new Person();
alert( typeof (Person.constructor));
alert(o.constructor);
alert(Person.constructor);
alert(Function.constructor);
alert(Object.constructor)
alert( new Date().constructor);
alert(Date.constructor);
function Man() {
}
Man.prototype = new Person();
alert(Man.constructor);
o = new Man();
alert(o.constructor)
Man.prototype.constructor = Man;
alert(o.constructor)
// -->
SCRIPT. >
如以上程式碼,可以知道
a、constructor的型別是函式;
b、javascript內部實現了很多函式,如Object,Date都是函式由Function得到的
。
c、用原型物件實現的繼承中,也要設定子類的constructor。如果你的程式中用
到了constructor,可能會出錯。
這是關於原型鏈的一系列的現象與原理的解釋以及例子
【轉載請註明出處與地址】
分成4個部分闡述: 1.如何建立一個物件
2.使用原型鏈prototype實現物件的繼承.
3.原型鏈上屬性和方法的聯絡與規則
4.深入剖析原型鏈。
一、如何建立一個物件:
1.使用關鍵字new建立。
var bj=new Object;
或者
function c()
{}
var bj=new c();
2.使用花括號建立。
var bj={};
我們知道,function是定義一個函式,裡面的var 都是區域性變數,那麼如何宣告一個物件的屬性和方法呢。祕密就在於this關鍵字。在函式內部使用this關鍵字,就相當於是物件的屬性和方法的宣告這個變數是存在的。具體的例子如下:
function a()
{
this.a=1;
this.get=function()
{
return this.a;
}
}
那麼只要使用用var bj =new a();即建立一個物件的例項,裡面就包括function裡面的this屬性和方法的就會直接拷貝到物件變數obj上。直接使用a.a的方法即可呼叫相應的屬性和方法。
但是注意,除非例項化function a(),否則無法呼叫裡面的this關鍵字指向的屬性以及方法。
因為此時this上定義的屬性以及方法類似於C++/JAVA等物件導向宣告的屬性以及方法。因此,這種定義函式的方法也被稱為JS的建構函式。
二、使用原型鏈prototype實現物件的繼承.
所有函式function都有一個預設的屬性prototype,通過用typeof函式測試我們可以看到prototype是一個物件。
alert(typeof(a.prototype));
結果:
那麼這個prototype的屬性到底有什麼用呢~?
既然是一個物件那麼賦值的時候就當然是付給prototype一個例項了。事實上,只要把一個例項賦給prototype這個屬性,那麼這個function就具有這個例項的屬性和方法。相當於我們常說的繼承。請看下面的程式碼:
function a()
{
this.ia=1;
this.get=function()
{
return this.ia;
}
}
function b() //b建構函式
{
this.ic=10;
this.getfatherclass=function()
{
return this.ic;
}
}
b.prototype=new a();//繼承例項。
var bj =new b();//例項化函式b
alert(obj.get());
就是這麼簡單的邏輯,我們就是使用物件b例項化的obj,輸出繼承了父類a的方法get。輸出父類a裡面的屬性ia。大家可以看到,這樣就輕鬆地實現了繼承。
那如果a又繼承另外一個物件x怎麼辦?
答案當然是b會繼承a所繼承的所有東西。而他們的關係就像用prototype屬性連起來一樣。所以我們可以看成也是一個由prototype連線而成的連結串列,人稱原型鏈。
三、原型鏈上屬性和方法的聯絡與規則
首先我們明確一個概念,原型鏈就是一串好像用屬性prototype連線而成的,每個節點具有屬性和方法甚至函式過程的鏈。這種鏈式結構我們無疑可以發現一個很重要的問題----鏈上的節點之間發生屬性或者方法重名怎麼辦?下面我們從讀和寫兩方面來解析這條原型鏈的屬性和方法的使用過程。
讀:
從上面的例子裡,alert(obj.get())輸出的結果是什麼呢,如果執行過一次,我們可以發現輸出的結果是1.
但是我們可以看到,建構函式b定義的時候是沒有get的方法的,同時,get方法呼叫的屬性ia也是b中不存在的,那麼我們在擁有prototype繼承的建構函式所例項化的物件是如何呼叫屬性和方法的呢。
結論是:對於呼叫例項化某一建構函式的物件的方法或者屬性的時候,js會在原型鏈 最結尾的點,也就是C++/JAVA上所描述的子類,上述例子中的b函式上,查詢是否有所需要的方法或者屬性,如果找不到的話,就會根據賦值給 prototype的父節點物件,父建構函式上查詢對應的屬性和方法,就這樣按照這種規則查詢出對應名稱的屬性或方法。如果鏈上節點都不存在的話,當然就 會輸出undefined。
如果子節點有該屬性或者方法,同時父節點也有的時候,當然會優先考慮子節點的,這也類似於C++/JAVA上所描述的覆蓋的概念,子類的屬性或者方法覆蓋父類的。
而 在上述例子中,get方法呼叫了一個this.ia,那麼查詢此此元素的時候,也會遵循上訴的原則,先查詢建構函式b上是否有屬性ia,沒有的話再查詢a 上的建構函式上的屬性ia。最後輸出結果1。如果我們在建構函式b上新增一個屬性this.ia=2,那麼結果會如上述結論所說輸出2.
寫:
我 們都知道,new操作會建立一個和建構函式一模一樣的副本,開闢一個空間存放物件。但是我們從上面的操作可以看到,對於函式鏈上的屬性,都可以被各個例項 所訪問,那麼,js在例項化有prototype繼承的建構函式的時候,是把鏈上的節點都例項化一次,還是被所有例項所共享呢?下面我們看下面例子。
function a()
{
this.ia=1;
this.get=function()
{
return this.ia;
}
}
function b() //b建構函式
{
this.ic=10;
this.getfatherclass=function()
{
return this.ic;
}
}
b.prototype=new a();//繼承例項。
var obj1 =new b();//例項化函式b
var obj2 =new b();//例項化函式b
alert(obj1.ia);//輸出1,因為他是父類a中的ia
alert(obj2.ia);//輸出1,因為他是父類a中的ia
obj1.ia=2;
alert(obj1.ia);//輸出2,因為寫操作會在例項物件建立一個副本ia並且賦值2,無修改其原型鏈上的父節點。
alert(obj2.ia);//輸出還是1,因為他是父類a中的ia
b.prototype.ia=3;//修改原型鏈上的屬性ia。
alert(obj1.ia);//輸出2,因為已經在例項上建立了副本,所以優先讀取副本。
alert(obj2.ia);//輸出3,因為子類b中不存在,所以還是查詢父節點(prototype物件上的物件)上的ia,由於父節點上的ia已經被修改成3,所以輸出3.
上面的例子說明了2件事:
1.寫操作並不是直接查詢原型鏈上的屬性,然後更改他,而是直接在例項上建立在一個同名物件。由於例項上已經存在這個屬性,所以下次讀取的時候優先讀取這個已存在的,相當於原型鏈上的原屬性被覆蓋(但是還是存在的)。
2.Prototype鏈上的節點(父類)都是被子類例項化後的物件所共享,也就是隻有唯一的一個物件prototype,因此當我們修改b.prototype.ia=3;/後,obj2輸出的也是3,說明他是被其他沒有建立ia屬性副本的例項所共享的。
這樣我們就清晰了,原型鏈只有一條,並且不會因為子類例項化和把鏈上的節點,屬性與方法都例項化,他將被所有子類的例項化物件所共享。
而這個原型鏈上的父類是準讀不準寫(通過子類例項化物件改變鏈上節點的屬性與方法)。一旦你的例項化物件進行和父類屬性與方法同名的寫操作時,就會在例項化物件上建立對應的副本,同時進行寫操作,此時這個例項也覆蓋了原型鏈上的這個屬性和方法。這種方法很好的保護了原型鏈上節點的共享性,不用擔心不小心被其他例項所修改。這樣大大地節省了記憶體,可以在頻繁的物件建立中,有效地減少了記憶體空間的使用。
當然可以通過函式b上的prototype物件來修改鏈上的屬性與方法,這樣就可以做到一改全改,讓子類共享的屬性發生改變。如上面的 b.prototype.ia=3;操作。
這種做法也衍生出一種叫做延時繫結的思想,在使用的時侯才進行屬性和方法的繫結,這種行為是可以通過原型鏈、 prototype這個屬性實現的。這樣可以很方便地應用到你的程式設計中,但是注意,這種方法還是弊大於利的,因為可以在任何地方進行重繫結。所以程式者也 許不知道在何時進行了繫結,使得程式碼很混亂,同時在大程式碼量的情況下,照成不可預料的結果。因此,延時繫結必須慎用。
四、深入剖析原型鏈。
上面說到,原型鏈是由prototype連在一起的,事實上這種說法是不嚴謹,甚至可以說是錯誤的,因為prototype是一個物件,並且例項是沒有prototype這個屬性的,但是,有比prototype更好的,用來表現原型鏈的東西,這是物件例項後的一個私有屬性,ie下是不可訪問的,但是用firefox是可以檢視的,它才是真正的作為例項在原型鏈上查詢屬性與方法的一個類似指標的屬性。可以這麼說,prototype是函式所有的,而__proto__是例項化的物件存在的屬性。我們常把這個__proto__指向的原型成為父原型。因為prototype是一個物件的例項,所以原型也有父原型。下面給出一段例子來解釋這個現象。
function a()
{
this.ia=1;
this.get=function()
{
return this.ia;
}
}
function b() //b建構函式
{
this.ic=10;
this.getfatherclass=function()
{
return this.ic;
}
}
b.prototype=new a();//繼承例項。
alert(a instanceof Function);//a是Function的例項;
alert(a.__proto__ === Function.prototype);//a的父原型指向到Function的原型;
alert(b.__proto__ === Function.prototype);//a的父原型指向到Function的原型
var bj =new b();//例項化函式b
alert(typeof(obj.__proto__));//輸出obejct,說明是一個物件
alert(obj instanceof Object);//ture.obj是其中一個物件
alert(obj.__proto__===b.prototype);//ture。因為__prototype指向父原型
alert(obj.__proto__.__proto__===a.prototype);//ture。因為__prototype指向父原型
alert(obj.__proto__.__proto__.__proto__===Object.prototype);//ture。因為__prototype指向父原型
alert(Object.prototype.__proto__);//null。因為Object的原型是所有父原型的頂端,它不再具有父原型;
alert(Function instanceof Object);//Function是Object的例項;
alert(Function.__proto__ === Function.prototype);//Function的父原型指向到Function的原型;
alert(Function.prototype.__proto__ === Object.prototype);//Function的原型的父原型指向到Object的原型
alert(Object.__proto__ === Function.prototype);//Object的父原型指向到Function的原型;
從上面的例子我們再也清晰不過了,例項查詢原型鏈的時候使用的__proto__ 這個私有屬性,通過這個屬性一層一層地遍歷原型鏈,而原型鏈的最終父原型是Object.prototype,它是所有例項的父原型,只要修改這個父原型,那麼所有的例項都將用到修改的屬性。
同時。Function.prototype是所有函式的父原型,而它Function.prototype的父原型也是Object.prototype。到了這裡,我們可以清晰地看到一個例項後面的原型鏈的全貌了。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/25897606/viewspace-704294/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- JS原型和原型鏈JS原型
- JS 原型與原型鏈JS原型
- js原型及原型鏈JS原型
- JS原型與原型鏈JS原型
- js原型鏈JS原型
- JS原型鏈理解JS原型
- JS中的原型與原型鏈JS原型
- JS原型與原型鏈圖解JS原型圖解
- 理解js中的原型,原型物件,原型鏈JS原型物件
- 深入理解JS原型與原型鏈JS原型
- 面試之JS篇 - 原型與原型鏈面試JS原型
- js回顧:原型鏈JS原型
- JS原型鏈繼承JS原型繼承
- js原型鏈圖解JS原型圖解
- js 原型鏈詳解JS原型
- 巧解 JS 原型鏈JS原型
- 淺析JS原型鏈JS原型
- JS原型鏈、prototype、__proto__、原型鏈繼承詳解JS原型繼承
- js基礎--原型物件與原型物件鏈JS原型物件
- 前端筆記——JS基礎(原型&&原型鏈)前端筆記JS原型
- JS 中原型和原型鏈深入理解JS原型
- js原型鏈汙染詳解JS原型
- JS 原型鏈查詢 (上)JS原型
- JS基礎總結(2)——原型與原型鏈JS原型
- 說說JS中的原型物件和原型鏈JS原型物件
- 深入學習js之——原型和原型鏈#1JS原型
- js--原型和原型鏈相關問題JS原型
- 從js資料型別到原型原型鏈JS資料型別原型
- js總結(原型和原型鏈,閉包等)JS原型
- JS的原型鏈和繼承JS原型繼承
- 小議JS原型鏈、繼承JS原型繼承
- 【原型鏈汙染】Python與Js原型PythonJS
- JS建構函式,原型鏈,原型物件總結JS函式原型物件
- 淺談JS物件的建立、原型、原型鏈繼承JS物件原型繼承
- JS核心系列:淺談 原型物件和原型鏈JS原型物件
- 2024-05-10 js原型和原型鏈JS原型
- 2019 面試準備 - JS 原型與原型鏈面試JS原型
- 你也學得會的Js原型和原型鏈啦JS原型