摘要:解決方案借用構造函數組合繼承寄生組合式繼承原型鏈繼承欠圖一張從來說,實現對象的繼承,還是相當麻煩的。分析基類創建的值,然后派生類的構造函數再修改這個值。
繼承 inherit
class 是對原型繼承的一種語法糖的包裝。那相對于原型繼承,它有什么優點呢?
我們來先看一個典型的基于原型鏈繼承的例子。部分內容來自“Javascript高級程序設計”
function SuperType() { this.property = true; } SuperType.prototype.getSuperValue = function() { return this.property; } function SubType() { this.subProperty = false; } SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function() { return this.subProperty; } var instance = new SubType(); console.log(instance.getSuperValue()); // true console.log(instance instanceof Object); // true console.log(instance instanceof SuperType); // true console.log(instance instanceof SubType); // true
問題,當包含引用類型的值。
function SuperType() { this.colors = ["red", "blue", "green"]; } function SubType() { } SubType.prototype = new SuperType(); var instance = new SubType(); instance.colors.push("black"); var instance1 = new SubType(); instance1.colors.push("white"); console.log(instance.colors); // [ "red", "blue", "green", "black", "white" ] console.log(instance1.colors); // [ "red", "blue", "green", "black", "white" ]
解決方案:
借用構造函數
function SuperType() { this.colors = ["red", "blue", "green"]; } function SubType() { SuperType.call(this); } SubType.prototype = new SuperType(); var instance = new SubType(); instance.colors.push("black"); var instance1 = new SubType(); instance1.colors.push("white"); console.log(instance.colors); console.log(instance1.colors);
組合繼承
function SuperType(name) { this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function() { console.log(this.name); } function SubType(name, age) { SuperType.call(this, name); this.age = age; } SubType.prototype = new SuperType(); SubType.prototype.sayAge = function() { console.log(this.age); }
寄生組合式繼承
function object(o) { function F() {} F.prototype = o; return new F(); } function inheritPrototype(subType, superType) { let prototype = object(superType.prototype); prototype.constructor = subType; subType.prototype = prototype; } function SuperType(name) { this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function() { console.log(this.name); } function SubType(name, age) { SuperType.call(this, name); this.age = age; } inheritPrototype(SubType, SuperType); SubType.prototype.sayAge = function() { console.log(this.age); } var instance = new SubType("Tom", 70); instance.colors.push("black"); var instance1 = new SubType("Jerry", 69); instance1.colors.push("white"); console.log(instance.colors); console.log(instance.sayName()); console.log(instance.sayAge()); console.log(instance1.colors); console.log(instance1.sayName()); console.log(instance1.sayAge());
MDN 原型鏈繼承
(欠圖一張)
從es5來說,實現對象的繼承,還是相當麻煩的。而extends 關鍵字的出現,使繼承變得簡單,原型會自動進行調整,super()/super關鍵字可以訪問父類的構造方法和屬性。
class Animal { constructor(name) { this.name = name; } speak() { console.log(this.name + " makes a noise."); } } class Dog extends Animal { speak() { console.log(this.name + " barks."); } } var d = new Dog("Mitzie"); d.speak();// "Mitzie barks."
分析:Dog類沒有構造函數,這樣合理嗎?
// 等價于上個類定義 class Dog extends Animal { constructor(name) { super(name) } speak() { console.log(this.name + " barks."); } }
super()方法調用注意:
只可在以extends 實現的派生類中的constructor方法中調用,在非派生類或方法中直接調用,會報錯。
在constructor中訪問this之前,一定要先調用super(),因為它負責初始化this,如果在super()調用之前嘗試訪問this,會報錯。
如果不想調用super(),則唯一的方法是讓類的constructor()返回一個對象。
類方法遮蔽說明:派生類中的方法總會覆蓋基類中的同名方法。
class Animal { constructor(name) { this.name = name; } speak() { console.log(this.name + " makes a noise."); } } class Dog extends Animal { speak() { console.log(this.name + " barks."); } } // 基類中的speak()方法被覆蓋靜態類成員繼承
說明:如果基類有靜態成員,那么這些靜態成員在派生類中也可用。
class Animal { constructor(name) { this.name = name; } speak() { console.log(this.name + " makes a noise."); } static create(name) { return new Animal(name); } } class Dog extends Animal { speak() { console.log(this.name + " barks."); } } let a1 = Animal.create("Monkey"); let a2 = Dog.create("BeijinDog"); console.log(a1 instanceof Animal); // true console.log(a2 instanceof Animal); // true console.log(a2 instanceof Dog); // false 這個是不是很意外?派生自表達式的類
由ES6的class定義可以知道,是function的語法糖,但為實現原型繼承,提供了方便的實現。JS的強大的一點就是函數可以返回函數,那如果返回類的定義呢?是否支持繼承?返回對象是個函數,并且有[[Constrcutor]]屬性和原型,就能滿足extends實現。
class Animal { constructor(name) { this.name = name; } speak() { console.log(this.name + " makes a noise."); } } function getBase() { return Animal; } class Dog extends getBase() { speak() { console.log(this.name + " barks."); } } const dog = new Dog("Tom"); dog.speak();
如果這個例子基于class的實現,有點取巧的意思,那看另一個例子。
const SerializableMixin = { serialize() { return JSON.stringify(this); } } const AnimalMixin = { speak() { console.log(this.name + " barks."); } } function mixin(...mixins) { const base = function() {}; Object.assign(base.prototype, ...mixins); return base; } class Dog extends mixin(AnimalMixin, SerializableMixin) { constructor(name){ super(name); this.name = name; } } const dog = new Dog("Tom"); dog.speak(); // Tom barks.
關于function,class,extends,mixin,是否有新的理解呢?
內建對象繼承在ES6之前,內建對象很難實現繼承的,更多用has-a思想,實現對內建對象的處理。ES6中,大量內建對象的內部實現得以暴漏,也使得繼承內建對象變成了可能。
class ColorsArray extends Array { } const colors = new ColorsArray(); colors[0] = "red"; console.log(colors.length); // 1 colors.length = 0; console.log(colors[0]); // undefined
分析:基類(Array)創建 this 的值,然后派生類的構造函數(ColorsArray)再修改這個值。所以一開始可以通過this訪問基類的所有內建功能,然后再正確地接收所有與之相關的功能。這與Array.apply/call 這種方法實現繼承的this處理方式正好相反。這也是extends特殊的地方。
Symbol.speciesclass ColorsArray extends Array { } const colors = new ColorsArray("red", "green", "blue"); const subColors = colors.slice(0,1); console.log(colors instanceof ColorsArray); // true console.log(subColors instanceof ColorsArray); // true
通常來講,slice 方法繼承自 Array ,返回的應該是Array的實例,但在這個示例中,卻返回的是ColorsArray的實例,這是為什么呢?這是ES6中Symbol.species的功勞。Symbol.species MDN 詳細說明
class MyArray extends Array { // Overwrite species to the parent Array constructor static get [Symbol.species]() { return Array; } } var a = new MyArray(1,2,3); var mapped = a.map(x => x * x); console.log(mapped instanceof MyArray); // false console.log(mapped instanceof Array); // true
注意:重寫實現的時候,使用getter+static,可以返回想用的類型,也可以返回 this,是的,你沒看錯,在static getter中使用了this,它指向的是MyArray的構造函數。
constructor中new.targetnew.target是es6中新添加的元屬性,只有通過new操作創建對象的時候,new.target才會被指向類/方法本身,通過call/apply操作,new.target為undefined。可以通過判斷new.target,來確實函數是否允許new操作。MDN new.target 說明
慣例,再加個代碼示例,偷懶,直接從MDN上拷了。
function Foo() { if (!new.target) throw "Foo() must be called with new"; console.log("Foo instantiated with new"); } new Foo(); // logs "Foo instantiated with new" Foo(); // throws "Foo() must be called with new"
又是先說function,不是已經升級到ES6,使用class了嗎?始終要有一個清楚的認識,class,是function實現原型繼承的語法糖,但有自己的特性存在的(不然,也不用引入class了)。
class A { constructor() { console.log(new.target.name); } } class B extends A { constructor() { super(); } } var a = new A(); // logs "A" var b = new B(); // logs "B" class C { constructor() { console.log(new.target); } } class D extends C { constructor() { super(); } } var c = new C(); // logs class C{constructor(){console.log(new.target);}} var d = new D(); // logs class D extends C{constructor(){super();}}
這個就是類的了。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/105822.html
摘要:接下來我們看下類的寫法,這個就很接近于傳統面向對象語言了。如果你想了解傳統面向對象語言,這里是一個好切入點。作為對象時,指向父類的原型對象。這些就是為將來在中支持面向對象的類機制而預留的。 在ES5中,我們經常使用方法或者對象去模擬類的使用,并基于原型實現繼承,雖然可以實現功能,但是代碼并不優雅,很多人還是傾向于用 class 來組織代碼,很多類庫、框架創造了自己的 API 來實現 c...
摘要:如果是在中,我們也許只能這樣做但是,在中,我們不僅可以在對象字面量屬性的定義中使用表達式,還有使用使用字符串模板析構擴展運算符我們在編寫組件的過程中,經常遇到要從父組件要把自己的很多屬性多傳給子組件的情況。 原文地址: http://babeljs.io/blog/2015/06/07/react-on-es6-plus/ showImg(http://7xiyp1.com1.z0.g...
摘要:請看對應版本干了什么可知,相當于以前在構造函數里的行為。這種寫法會與上文中寫法有何區別我們在環境下運行一下,看看這兩種構造函數的有何區別打印結果打印結果結合上文中關于原型的論述,仔細品味這兩者的差別,最好手動嘗試一下。 ES6 class 在ES6版本之前,JavaScript語言并沒有傳統面向對象語言的class寫法,ES6發布之后,Babel迅速跟進,廣大開發者也很快喜歡上ES6帶...
摘要:當我們在寫時候會用到中的語法比較常見的情況如下這里有兩個問題是否有必要在中調用函數調用和有何區別解答只有當你有一個時候調用才是必須的看代碼上述代碼完全符合規定所以你其實并沒有必要去為你創建的每個調用話分兩頭如果你的代碼中有你就必須調用出現上 當我們在寫React時候 會用到ES6中的class語法 ,比較常見的情況如下: class MyClass extends React.Comp...
摘要:以上的代碼對應到就是調用父類的值得注意的是關鍵字表示父類的構造函數,相當于的。舉個例子這是因為作為構造函數的語法糖,同時有屬性和屬性,因此同時存在兩條繼承鏈。子類的屬性,表示構造函數的繼承,總是指向父類。 前言 在上一篇 《 ES6 系列 Babel 是如何編譯 Class 的(上)》,我們知道了 Babel 是如何編譯 Class 的,這篇我們學習 Babel 是如何用 ES5 實現...
閱讀 2456·2019-08-30 15:53
閱讀 2572·2019-08-29 13:11
閱讀 2653·2019-08-29 12:45
閱讀 3485·2019-08-29 12:41
閱讀 2326·2019-08-26 10:14
閱讀 2153·2019-08-23 14:39
閱讀 2313·2019-08-23 12:38
閱讀 3377·2019-08-23 12:04