學習《JS高階程式設計》(3) 原型鏈

2021-08-10 18:30:02 字數 3863 閱讀 6192

ecmascript中描述了原型鏈的概念,並將原型鏈作為實現繼承的主要方法。其基本思想是利用原型讓乙個引用型別繼承另乙個引用型別的屬性和方法。簡單回顧一下建構函式、原型和例項的關係:每個建構函式都有乙個原型物件,原型物件都包含乙個指向建構函式的指標,而例項都包含乙個指向原型物件的內部指標。那麼,假如我們讓原型物件等於另乙個型別的例項,結果會怎麼樣呢?顯然,此時的原型物件將包含乙個指向另乙個原型的指標,相應地,另乙個原型中也包含著乙個指向另乙個建構函式的指標。假如另乙個原型又是另乙個型別的例項,那麼上述關係依然成立,如此層層遞進,就構成了例項與原型的鏈條。這就是所謂原型鏈的基本概念。

實現原型鏈有一種基本模式, 其**大致如下。

function

supertype

()supertype.prototype.getsupervalue = function

();function

subtype

()//繼承了supertype

subtype.prototype = new supertype();

subtype.prototype.getsubvalue=function

();var instance = new subtype();

alert(instance.getsupervalue()); //true;

事實上,前面例子中展示的原型鏈還少一環。我們知道,所有引用型別預設都繼承了object,而這個繼承也是通過原型鏈實現的。大家要記住,所有函式的預設原型都是object的例項,因此預設原型都會包含乙個內部指標,指向object.prototype。這也正是所有自定義型別都會繼承tostring(),valueof()等預設方法的基本原因。所有,我們說上面例子展示的原型鏈中還應該包括另外乙個繼承層次。如圖

一句話,subtype繼承了supertype,而supertype繼承了object。當呼叫instance.tostring時,實際上呼叫的是儲存在object.prototype中的那個方法。

可以通過兩種方法來確定原型和例項直接的關係。第一種方式是使用instanceof操作符,只要用這個操作符來測試例項與原型鏈中出現過的建構函式,結果就會返回true。以下幾行**就說明了這一點。

alert(instance instanceof

object);//true

alert(instance instanceof supertype);//true

alert(instance instanceof subtype);//true

由於原型鏈的關係,我們可以說instance是object、supertype或subtype中任何乙個型別的例項。因此,測試這三個建構函式的結果都返回了true。

第二種方式是使用isprototypeof()方法。同樣,只要是原型鏈中出現過的原型,都可以說是該原型鏈所派生的例項的原型,因此isprototypeof()方法也會返回true,如此所示。

alert(object.prototype.isprototypeof(instance));//true

alert(supertype.prototype.isprototypeof(instance));//true

alert(subtype.prototype.isprototypeof(instance));//true

子型別有時候需要覆蓋超型別中的某個方法,或者需要新增超型別中不存在的某個方法。但不管怎樣,給原型新增方法的**一定要放在替換原型的語句之後。

function

supertype

()supertype.prototype.getsupervalue = function

();function

subtype

()//繼承了supertype

subtype.prototype = new supertype();

//新增新方法

subtype.prototype.getsubvalue=function

();//重寫超型別中的方法

subtype.prototype.getsupervalue=function

();var instance = new subtype();

alert(instance.getsupervalue()); //false;

重寫的方法getsupervalue()是原型鏈中已經存在的乙個方法,但重寫這個方法將會遮蔽原來的那個方法。換句話說,當通過subtype的例項呼叫getsupervalue()時,呼叫的就是這個重新定義的方法;但通過supertype的例項呼叫getsupervalue()時,還會繼續呼叫原來的那個方法。這裡要格外注意的是,必須在用supertype的例項替換原型之後,再定義這兩個方法。

還有一點需要注意,即在通過原型鏈實現繼承時,不能使用物件字面量建立原型方法。因為這樣做就會重寫原型鏈。

原型鏈雖然很強大,可以用它來實現繼承,但它也存在一些問題。其中,最主要的問題來自包含引用型別值的原型。想必大家還記得,我們前面介紹過包含引用型別值的原型屬性會被所有例項共享;而這也正是為什麼要在建構函式中,而不是在原型物件中定義屬性的原因。在通過原型來實現繼承時,原型實際上會變成另乙個型別的例項。於是,原先的例項屬性也就順理成章地變成了現在的原型屬性了。

下列**可以說明這個問題

function

supertype

();}

function

subtype

()//繼承了supertype

subtype.prototype=new supertype();

var instance1 = new subtype();

instance1.colors.push("black");

alert(instance1.colors; //"red,blue,green.black"

var instance2 = new subtype();

alert(instance1.colors; //"red,blue,green.black"

這個例子中的supertype建構函式定義了乙個colors屬性,該屬性包含乙個陣列(引用型別值)。supertype的每個例項都會有各自包含自己陣列的colors屬性。當subtype通過原型鏈繼承了supertype之後,subtype之後,subtype.prototype就變成supertype的乙個例項,因此它也擁有了乙個它自己的colors屬性——就跟專門建立了乙個subtype.prototype.colors屬性一樣。但結果是什麼呢?結果是subtype的所有例項都會共享這乙個colors屬性。而我們對instance1.colors的修改能夠通過instance2.colors反映出來,就已經充分證明了這一點。

原型鏈的第二個問題是:在建立子型別的例項時,不能向超型別的建構函式中傳遞引數。實際上,應該說是沒有辦法在不影響所有物件例項的情況下,給超型別的建構函式傳遞引數。

原型鏈實際上是將乙個函式a的原型宣告為另乙個函式b的例項,因此這個原型包含了a的所有變數並且存在乙個[[prototype]]屬性指向b的prototype。根據作用域鏈的作用,作用域鏈會從兩個維度來搜尋。

1. 首先在原本的作用域鏈

2. 每乙個鏈結點的作用域的鏈(如果這個鏈結點是有prototype的話)

這樣a元素就能訪問到b元素prototype裡的方法。從而實現繼承。

JS高階 原型鏈

大綱 主體 1 建立函式 注意 object內建原生物件原來就有2 新增例項方法 3 根據建構函式建立例項物件 原型鏈尋找 1 本身有在本身找 2 本身沒有往摸著隱式原型鏈往裡找 或者再上層 4 原型鏈盡頭 object.prototype.prop 為null 5 原型鏈理解 本質為隱式原型鏈 小...

JS高階 原型和原型鏈

原型鏈是一種關係,例項物件和原型物件之間的關係,關係是通過例項物件中瀏覽器使用的原型 proto 來聯絡的 自定義建構函式,通過例項化,建立例項物件 例項物件中 proto 是原型,瀏覽器使用的 建構函式中的prototype是原型,程式設計師使用的 使用物件 使用物件中的屬性和物件中的方法,使用物...

js 高階 原型與原型鏈

所有函式都有乙個特別的屬性 prototype 顯式原型屬性 所有例項物件都有乙個特別的屬性 proto 隱式原型屬性 1.每個函式都有乙個prototype屬性,它預設指向乙個物件 objectg 即稱為 原型物件 顯式原型與隱式原型的關係 函式的prototype 定義函式時被自動賦值,值預設為...