摘要:解決方案借用構(gòu)造函數(shù)組合繼承寄生組合式繼承原型鏈繼承欠圖一張從來說,實(shí)現(xiàn)對象的繼承,還是相當(dāng)麻煩的。分析基類創(chuàng)建的值,然后派生類的構(gòu)造函數(shù)再修改這個(gè)值。
繼承 inherit
class 是對原型繼承的一種語法糖的包裝。那相對于原型繼承,它有什么優(yōu)點(diǎn)呢?
我們來先看一個(gè)典型的基于原型鏈繼承的例子。部分內(nèi)容來自“Javascript高級(jí)程序設(shè)計(jì)”
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
問題,當(dāng)包含引用類型的值。
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" ]
解決方案:
借用構(gòu)造函數(shù)
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來說,實(shí)現(xiàn)對象的繼承,還是相當(dāng)麻煩的。而extends 關(guān)鍵字的出現(xiàn),使繼承變得簡單,原型會(huì)自動(dòng)進(jìn)行調(diào)整,super()/super關(guān)鍵字可以訪問父類的構(gòu)造方法和屬性。
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類沒有構(gòu)造函數(shù),這樣合理嗎?
// 等價(jià)于上個(gè)類定義 class Dog extends Animal { constructor(name) { super(name) } speak() { console.log(this.name + " barks."); } }
super()方法調(diào)用注意:
只可在以extends 實(shí)現(xiàn)的派生類中的constructor方法中調(diào)用,在非派生類或方法中直接調(diào)用,會(huì)報(bào)錯(cuò)。
在constructor中訪問this之前,一定要先調(diào)用super(),因?yàn)樗?fù)責(zé)初始化this,如果在super()調(diào)用之前嘗試訪問this,會(huì)報(bào)錯(cuò)。
如果不想調(diào)用super(),則唯一的方法是讓類的constructor()返回一個(gè)對象。
類方法遮蔽說明:派生類中的方法總會(huì)覆蓋基類中的同名方法。
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()方法被覆蓋靜態(tài)類成員繼承
說明:如果基類有靜態(tài)成員,那么這些靜態(tài)成員在派生類中也可用。
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 這個(gè)是不是很意外?派生自表達(dá)式的類
由ES6的class定義可以知道,是function的語法糖,但為實(shí)現(xiàn)原型繼承,提供了方便的實(shí)現(xiàn)。JS的強(qiáng)大的一點(diǎn)就是函數(shù)可以返回函數(shù),那如果返回類的定義呢?是否支持繼承?返回對象是個(gè)函數(shù),并且有[[Constrcutor]]屬性和原型,就能滿足extends實(shí)現(xiàn)。
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();
如果這個(gè)例子基于class的實(shí)現(xiàn),有點(diǎn)取巧的意思,那看另一個(gè)例子。
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.
關(guān)于function,class,extends,mixin,是否有新的理解呢?
內(nèi)建對象繼承在ES6之前,內(nèi)建對象很難實(shí)現(xiàn)繼承的,更多用has-a思想,實(shí)現(xiàn)對內(nèi)建對象的處理。ES6中,大量內(nèi)建對象的內(nèi)部實(shí)現(xiàn)得以暴漏,也使得繼承內(nèi)建對象變成了可能。
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)創(chuàng)建 this 的值,然后派生類的構(gòu)造函數(shù)(ColorsArray)再修改這個(gè)值。所以一開始可以通過this訪問基類的所有內(nèi)建功能,然后再正確地接收所有與之相關(guān)的功能。這與Array.apply/call 這種方法實(shí)現(xiàn)繼承的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 ,返回的應(yīng)該是Array的實(shí)例,但在這個(gè)示例中,卻返回的是ColorsArray的實(shí)例,這是為什么呢?這是ES6中Symbol.species的功勞。Symbol.species MDN 詳細(xì)說明
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
注意:重寫實(shí)現(xiàn)的時(shí)候,使用getter+static,可以返回想用的類型,也可以返回 this,是的,你沒看錯(cuò),在static getter中使用了this,它指向的是MyArray的構(gòu)造函數(shù)。
constructor中new.targetnew.target是es6中新添加的元屬性,只有通過new操作創(chuàng)建對象的時(shí)候,new.target才會(huì)被指向類/方法本身,通過call/apply操作,new.target為undefined。可以通過判斷new.target,來確實(shí)函數(shù)是否允許new操作。MDN new.target 說明
慣例,再加個(gè)代碼示例,偷懶,直接從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,不是已經(jīng)升級(jí)到ES6,使用class了嗎?始終要有一個(gè)清楚的認(rèn)識(shí),class,是function實(shí)現(xiàn)原型繼承的語法糖,但有自己的特性存在的(不然,也不用引入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();}}
這個(gè)就是類的了。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/105822.html
摘要:接下來我們看下類的寫法,這個(gè)就很接近于傳統(tǒng)面向?qū)ο笳Z言了。如果你想了解傳統(tǒng)面向?qū)ο笳Z言,這里是一個(gè)好切入點(diǎn)。作為對象時(shí),指向父類的原型對象。這些就是為將來在中支持面向?qū)ο蟮念悪C(jī)制而預(yù)留的。 在ES5中,我們經(jīng)常使用方法或者對象去模擬類的使用,并基于原型實(shí)現(xiàn)繼承,雖然可以實(shí)現(xiàn)功能,但是代碼并不優(yōu)雅,很多人還是傾向于用 class 來組織代碼,很多類庫、框架創(chuàng)造了自己的 API 來實(shí)現(xiàn) c...
摘要:如果是在中,我們也許只能這樣做但是,在中,我們不僅可以在對象字面量屬性的定義中使用表達(dá)式,還有使用使用字符串模板析構(gòu)擴(kuò)展運(yùn)算符我們在編寫組件的過程中,經(jīng)常遇到要從父組件要把自己的很多屬性多傳給子組件的情況。 原文地址: http://babeljs.io/blog/2015/06/07/react-on-es6-plus/ showImg(http://7xiyp1.com1.z0.g...
摘要:請看對應(yīng)版本干了什么可知,相當(dāng)于以前在構(gòu)造函數(shù)里的行為。這種寫法會(huì)與上文中寫法有何區(qū)別我們在環(huán)境下運(yùn)行一下,看看這兩種構(gòu)造函數(shù)的有何區(qū)別打印結(jié)果打印結(jié)果結(jié)合上文中關(guān)于原型的論述,仔細(xì)品味這兩者的差別,最好手動(dòng)嘗試一下。 ES6 class 在ES6版本之前,JavaScript語言并沒有傳統(tǒng)面向?qū)ο笳Z言的class寫法,ES6發(fā)布之后,Babel迅速跟進(jìn),廣大開發(fā)者也很快喜歡上ES6帶...
摘要:當(dāng)我們在寫時(shí)候會(huì)用到中的語法比較常見的情況如下這里有兩個(gè)問題是否有必要在中調(diào)用函數(shù)調(diào)用和有何區(qū)別解答只有當(dāng)你有一個(gè)時(shí)候調(diào)用才是必須的看代碼上述代碼完全符合規(guī)定所以你其實(shí)并沒有必要去為你創(chuàng)建的每個(gè)調(diào)用話分兩頭如果你的代碼中有你就必須調(diào)用出現(xiàn)上 當(dāng)我們在寫React時(shí)候 會(huì)用到ES6中的class語法 ,比較常見的情況如下: class MyClass extends React.Comp...
摘要:以上的代碼對應(yīng)到就是調(diào)用父類的值得注意的是關(guān)鍵字表示父類的構(gòu)造函數(shù),相當(dāng)于的。舉個(gè)例子這是因?yàn)樽鳛闃?gòu)造函數(shù)的語法糖,同時(shí)有屬性和屬性,因此同時(shí)存在兩條繼承鏈。子類的屬性,表示構(gòu)造函數(shù)的繼承,總是指向父類。 前言 在上一篇 《 ES6 系列 Babel 是如何編譯 Class 的(上)》,我們知道了 Babel 是如何編譯 Class 的,這篇我們學(xué)習(xí) Babel 是如何用 ES5 實(shí)現(xiàn)...
閱讀 2468·2019-08-30 15:53
閱讀 2580·2019-08-29 13:11
閱讀 2668·2019-08-29 12:45
閱讀 3495·2019-08-29 12:41
閱讀 2337·2019-08-26 10:14
閱讀 2166·2019-08-23 14:39
閱讀 2319·2019-08-23 12:38
閱讀 3383·2019-08-23 12:04