2016-12-05 32 views
9

隨着ES5中的原型繼承,從Array繼承並得到預期的行爲似乎不是微不足道的,例如在向Array(見下面的代碼)添加項目時自動更新.length。 ES5創建派生函數的對象(),然後傳遞它的基類型進行初始化,爲什麼這個模型很難在此模型中獲得預期的行爲?爲什麼從Array中繼承很難在ES5中實現?

ES6在基礎構造函數中改變了行爲並創建了對象,派生類的構造函數在初始化之後(在致電super()後),想知道爲什麼這解決了問題。

function MyArray(){} 
 
MyArray.prototype = Object.create(Array.prototype); 
 

 
var myArr = new MyArray(); 
 
myArr[0] = 'first'; 
 
console.log(myArr.length); // expect '1', but got '0' in output

+0

ES5不支持從Array繼承此方式嘿沒有投入ES5)。當你嘗試時,你會失去'.length'的特殊功能,所以它不能正常工作。 ES6有能力子類化Array。 – jfriend00

+0

*「想知道爲什麼這解決了問題」*因爲超級構造函數('Array')創建了具有所有特殊內部行爲的對象,而不是子構造函數。 –

回答

5

關鍵事約Array是一個真正的數組對象是Array Exotic Object。一個奇特的對象是一個具有無法使用標準JS語言特性實現的行爲的對象,儘管在ES6 Proxy中允許用戶代碼創建類似異國情調的對象的能力更多。

當子類構造函數返回像Array這樣的奇怪對象時,子類化方法需要以這樣的方式完成,即創建的對象實際上是一個外來對象。當你這樣做

function ArraySubclass(){} 
ArraySubclass.prototype = Object.create(Array.prototype); 

然後

(new ArraySubclass()) instanceof Array 

因爲原型相匹配,但new ArraySubclass返回的對象僅僅是一個正常的對象恰好在其原型鏈Array.prototype。但你會注意到,

Array.isArray(new ArraySubclass()); // false 

因爲對象不是一個真正的奇特。在這種情況下

new ArraySubclass() 

所以在ES5你如何延長Array等同於做

var obj = Object.create(ArraySubclass.prototype); 
ArraySubclass.call(obj); 

?您需要創建一個奇特的對象,但是您還需要確保異國情調的對象在其原型鏈中有對象ArraySubclass.prototype。這就是ES5遇到的問題,因爲在ES5中,沒有辦法改變現有對象的原型。隨着__proto__擴展,很多搜索引擎添加你可以得到下面類似的代碼

var obj = new Array(); 
obj.__proto__ = ArraySubclass.prototype; 
ArraySubclass.call(obj); 

說你想概括上面的圖案,你會怎麼做正確的數組子類的行爲嗎?

function makeSubclass(baseConstructor, childConstructor){ 
    var obj = new baseConstructor(); 
    obj.__proto__ = childConstructor.prototype; 
    return obj; 
} 

function ArraySubclass(){ 
    var arr = makeSubclass(Array, ArraySubclass); 

    // do initialization stuff and use 'arr' like 'this' 

    return arr; 
} 
ArraySubclass.prototype = Object.create(Array.prototype); 

這樣在ES5 + __proto__中工作,但是情況會變得更復雜嗎?如果你想子類ArraySubclass怎麼辦?你必須能夠改變第二個參數makeSubclass。但我們如何做到這一點?這裏的實際目標是什麼?當你這樣做

new ArraySubclass() 

它傳遞給我們關心作爲第二個參數new的值,它是一個應該得到傳承下去該構造函數的原型。 ES5沒有很好的途徑來完成這個任務。

這是ES6課程的好處。

class ArraySubclass extends Array { 
    constructor(){ 
    super(); 
    } 
} 

關鍵的一點是,當super()運行時,它知道ArraySubclass是子類。當super()調用new Array時,它還傳遞了一個額外的隱藏參數,它說:「嘿,當你創建這個數組時,將它的原型設置爲ArraySubclass.prototype。如果有很多級別的繼承,它將傳遞最小的原型,返回的外來物體是一個真正的異國情調,同時也確保它具有正確的原型

這不僅意味着事物構造正確,而且意味着引擎可以創建具有正確原型值的對象。對象的__proto__創建後的值是一個衆所周知的去優化點,因爲引擎處理和跟蹤對象的方式不同。

+0

是最小的構造函數'new.target'嗎?如果是這樣,'new.target'使隱含的隱藏參數變得容易? – Thomson

+0

是的,函數聲明中的'new.target'基本上就是你想要的,但是如果你使用標準函數聲明方法,你仍然需要一種方法來正確構造對象並調用父構造函數,這並不容易。 – loganfsmyth