js原型和繼承

2021-08-13 21:14:48 字數 3605 閱讀 8705

一. 原型與建構函式

js所有的函式都有乙個prototype屬性,這個屬性引用了乙個物件,即原型物件,也簡稱原型。這個函式包括建構函式和普通函式,我們講的更多是建構函式的原型,但是也不能否定普通函式也有原型。譬如普通函式:

function

f()alert(f.prototype

instanceof

object)

//true

建構函式,也即構造物件。首先了解下通過建構函式例項化物件的過程。

function

a(x)

var obj

=newa(1

);

例項化obj物件有三步:

1. 建立obj物件:obj=new object();

2. 將obj的內部__proto__指向構造他的函式a的prototype,同時,obj.constructor===a.prototype.constructor(這個是永遠成立的,即使a.prototype不再指向原來的a原型,也就是說:類的例項物件的constructor屬性永遠指向」建構函式」的prototype.constructor),從而使得obj.constructor.prototype指向a.prototype(obj.constructor.prototype===a.prototype,當a.prototype改變時則不成立,下文有遇到)。obj.constructor.prototype與的內部_proto_是兩碼事,例項化物件時用的是_proto_,obj是沒有prototype屬性的,但是有內部的__proto__,通過__proto__來取得原型鏈上的原型屬性和原型方法,firefox公開了__proto__,可以在firefox中alert(obj.__proto__);

3. 將obj作為this去呼叫建構函式a,從而設定成員(即物件屬性和物件方法)並初始化。

當這3步完成,這個obj物件就與建構函式a再無聯絡,這個時候即使建構函式a再加任何成員,都不再影響已經例項化的obj物件了。此時,obj物件具有了x屬性,同時具有了建構函式a的原型物件的所有成員,當然,此時該原型物件是沒有成員的。

原型物件初始是空的,也就是沒有乙個成員(即原型屬性和原型方法)。可以通過如下方法驗證原型物件具有多少成員。

var

num=0;

for (o

ina.prototype)

alert(

」member: 」+

num);

//alert出原型所有成員個數。

但是,一旦定義了原型屬性或原型方法,則所有通過該建構函式例項化出來的所有物件,都繼承了這些原型屬性和原型方法,這是通過內部的_proto_鏈來實現的。

譬如a.prototype.say=function();

那所有的a的物件都具有了say方法,這個原型物件的say方法是唯一的副本給大家共享的,而不是每乙個物件都有關於say方法的乙個副本。

二. 原型與繼承

首先,看個簡單的繼承實現。

1 

function

a(x)

4  

function

b(x,y)

這兩種方法都有將this傳遞到a的執行裡,this指向的是b的物件,這就是為什麼不直接a(x)的原因。這種繼承方式即是類繼承(js沒有類,這裡只是指建構函式),雖然繼承了a構造物件的所有屬性方法,但是不能繼承a的原型物件的成員。而要實現這個目的,就是在此基礎上再新增原型繼承。

通過下面的例子,就能很深入地了解原型,以及原型參與實現的完美繼承。(本文核心在此^_^)

1 

function

a(x)

4 a.prototype.a ="

a"

;5

function

b(x,y)

9 b.prototype.b1

=function

()12 b.prototype

=new

a();

13 b.prototype.b2

=function

()16 b.prototype.constructor =b;

17

varobj

=newb(1

,3

);

這個例子講的就是b繼承a。第7行類繼承:a.call(this.x);上面已講過。實現原型繼承的是第12行:b.prototype = new a();

就是說把b的原型指向了a的1個例項物件,這個例項物件具有x屬性,為undefined,還具有a屬性,值為」a」。所以b原型也具有了這2個屬性(或者說,b和a建立了原型鏈,b是a的下級)。而因為方才的類繼承,b的例項物件也具有了x屬性,也就是說obj物件有2個同名的x屬性,此時原型屬性x要讓位於例項物件屬性x,所以obj.x是1,而非undefined。第13行又定義了原型方法b2,所以b原型也具有了b2。雖然第9~11行設定了原型方法b1,但是你會發現第12行執行後,b原型不再具有b1方法,也就是obj.b1是undefined。因為第12行使得b原型指向改變,原來具有b1的原型物件被拋棄,自然就沒有b1了。

第12行執行完後,b原型(b.prototype)指向了a的例項物件,而a的例項物件的構造器是建構函式a,所以b.prototype.constructor就是構造物件a了(換句話說,a構造了b的原型)。

alert(b.prototype.constructor)出來後就是」function a(x)」 。同樣地,obj.constructor也是a構造物件,alert(obj.constructor)出來後就是」function a(x)」 ,也就是說b.prototype.constructor===obj.constructor(true),但是b.prototype===obj.constructor.prototype(false),因為前者是b的原型,具有成員:x,a,b2,後者是a的原型,具有成員:a。如何修正這個問題呢,就在第16行,將b原型的構造器重新指向了b建構函式,那麼b.prototype===obj.constructor.prototype(true),都具有成員:x,a,b2。

如果沒有第16行,那是不是obj = new b(1,3)會去呼叫a建構函式例項化呢?答案是否定的,你會發現obj.y=3,所以仍然是呼叫的b建構函式例項化的。雖然obj.constructor===a(true),但是對於new b()的行為來說,執行了上面所說的通過建構函式建立例項物件的3個步驟,第一步,建立空物件;第二步,obj.__proto__ === b.prototype,b.prototype是具有x,a,b2成員的,obj.constructor指向了b.prototype.constructor,即建構函式a;第三步,呼叫的建構函式b去設定和初始化成員,具有了屬性x,y。雖然不加16行不影響obj的屬性,但如上一段說,卻影響obj.constructor和obj.constructor.prototype。所以在使用了原型繼承後,要進行修正的操作。

關於第12、16行,總言之,第12行使得b原型繼承了a的原型物件的所有成員,但是也使得b的例項物件的構造器的原型指向了a原型,所以要通過第16行修正這個缺陷。

JS原型繼承和call繼承

首先建立animal和bird兩個構造器 var animal function var bird function 怎麼能在bird中同樣實現animal共有的eat屬性?var bird function this fly function 這種直接複製 實現的方式很low,而且很費事 原型繼承...

JS的原型和繼承

除null和undefined,js中的所有資料型別都有這個屬性 它表示當我們訪問乙個物件的某個屬性時,如果該物件自身不存在該屬性,就從它的 proto 屬性上繼續查詢,以此類推,直到找到,若找到最後還是沒有找到,則結果為undefined 我們把乙個物件的 proto 屬性所指向的物件叫該物件的原...

JS的原型和繼承

proto 除null和undefined,js中的所有資料型別都有這個屬性 它表示當我們訪問乙個物件的某個屬性時,如果該物件自身不存在該屬性,就從它的 proto 屬性上繼續查詢,以此類推,直到找到,若找到最後還是沒有找到,則結果為undefined 我們把乙個物件的 proto 屬性所指向的物件...