一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

ES6 類(Class)的繼承(extends)和自定義存(setter)取值(getter)詳解

 霞客書齋 2017-10-24


ES6 類(Class)的繼承(extends)和自定義存(setter)取值(getter)詳解
ES6的Class之間可以通過(guò)extends關(guān)鍵字實(shí)現(xiàn)繼承,這比ES5的通過(guò)修改原型鏈實(shí)現(xiàn)繼承,要簡(jiǎn)單很多,這也是平常大多數(shù)面向?qū)ο笳Z(yǔ)言的方式。
1.類的super方法
子類必須在constructor方法中調(diào)用super方法,否則新建實(shí)例時(shí)會(huì)報(bào)錯(cuò)。如果子類在constructor方法中使用了this初始化實(shí)例屬性,調(diào)用super方法必須放到this初始化實(shí)例屬性的前面。這是因?yàn)樽宇悰](méi)有自己的this對(duì)象,而是繼承父類的this對(duì)象,然后對(duì)其進(jìn)行加工。如果不調(diào)用super方法,子類就得不到this對(duì)象。
[javascript] view plain copy
  1. //ExtendStu.js  
  2. //正確   構(gòu)造  
  3. constructor(name, age, height) {  
  4.     super(name,age);  
  5.     this.height = height;  
  6. }  
  7.   
  8. //錯(cuò)誤  構(gòu)造  
  9. constructor(name, age, height) {  
  10.     this.height = height;  
  11.     super(name,age);  
  12.     //SyntaxError: 'this' is not allowed before super()  
  13. }  
ES5的繼承,實(shí)質(zhì)是先創(chuàng)造子類的實(shí)例對(duì)象this,然后再將父類的方法添加到this上面(Parent.apply(this))。ES6的繼承機(jī)制完全不同,實(shí)質(zhì)是先創(chuàng)造父類的實(shí)例對(duì)象this(所以必須先調(diào)用super方法),然后再用子類的構(gòu)造函數(shù)修改this。如果子類沒(méi)有定義constructor方法,這個(gè)方法會(huì)被默認(rèn)添加。
(1)super作為函數(shù)時(shí),指向父類的構(gòu)造函數(shù)。super()只能用在子類的構(gòu)造函數(shù)之中,用在其他地方就會(huì)報(bào)錯(cuò)。
注意:super雖然代表了父類ExtendStuParent的構(gòu)造函數(shù),但是返回的是子類ExtendStu的實(shí)例,即super內(nèi)部的this指的是ExtendStu,因此super()在這里相當(dāng)于ExtendStuParent.prototype.constructor.call(this)。
(2)super作為對(duì)象時(shí),指向父類的原型對(duì)象。
[javascript] view plain copy
  1. toString(){  
  2.     return "name:"+ super.getName() + " age:"+this.age + " height:"+this.height;  
  3. }  
上面代碼中,子類ExtendStu當(dāng)中的super.getName(),就是將super當(dāng)作一個(gè)對(duì)象使用。這時(shí),super指向ExtendStuParent.prototype,所以super.getName()就相當(dāng)于ExtendStuParent.prototype.getName()。
由于super指向父類的原型對(duì)象,所以定義在父類實(shí)例上的屬性,是無(wú)法通過(guò)super調(diào)用的。

[javascript] view plain copy
  1. toString(){  
  2.     console.log(super.age);//undefined  
  3.     return "name:"+ super.getName() + " age:"+this.age + " height:"+this.height;  
  4. }  
age是父類的實(shí)例屬性,子類通過(guò)是super不能調(diào)用
[javascript] view plain copy
  1. //ExtendStuParent.js  
  2. ExtendStuParent.prototype.width = '30cm';  
  3. //ExtendStu.js  
  4. toString(){  
  5.     console.log(super.age);//undefined  
  6.     console.log(super.width);//30cm  
  7.     return "name:"+ super.getName() + " age:"+this.age + " height:"+this.height;  
  8. }  
width定義在父類的原型對(duì)象上,所以width可以通過(guò)super取到。

ES6 規(guī)定,通過(guò)super調(diào)用父類的方法時(shí),super會(huì)綁定子類的this。由于綁定子類的this,所以如果通過(guò)super對(duì)某個(gè)屬性賦值,這時(shí)super就是this,賦值的屬性會(huì)變成子類實(shí)例的屬性。

[javascript] view plain copy
  1. // 構(gòu)造  
  2. constructor(name, age, height) {  
  3.     super(name,age);  
  4.     this.height = height;  
  5.     //父類和子類都有實(shí)例屬性name,但是在toString方法調(diào)用getName的時(shí)候,  
  6.     //返回的是子類的的數(shù)據(jù)  
  7.     this.name = 'LiSi 子類';  
  8.   
  9.     //  
  10.     this.color = 'black';  
  11.     super.color = 'white';  
  12.     console.log(super.color);//undefined  
  13.     console.log(this.color);//black  
  14. }  
最后,由于對(duì)象總是繼承其他對(duì)象的,所以可以在任意一個(gè)對(duì)象中,使用super關(guān)鍵字。
[javascript] view plain copy
  1. import ExtendStuParent from './ExtendStuParent';//父類  
  2. import ExtendStu from './ExtendStu';//子類  
  3. let extendStu = new ExtendStu('LiSi',12,'170cm');  
  4. console.log(extendStu.toString());  
  5. console.log(extendStu instanceof ExtendStuParent);//true  
  6. console.log(extendStu instanceof ExtendStu);//true  
說(shuō)明;ExtendStu繼承ExtendStuParent之后,ExtendStu創(chuàng)建的對(duì)象同時(shí)是ExtendStu和ExtendStuParent兩個(gè)類的實(shí)例。

2.類的prototype屬性和__proto__屬性
Class作為構(gòu)造函數(shù)的語(yǔ)法糖,同時(shí)有prototype屬性和__proto__屬性,因此同時(shí)存在兩條繼承鏈。
(1)子類的__proto__屬性,表示構(gòu)造函數(shù)的繼承,總是指向父類。
(2)子類prototype屬性的__proto__屬性,表示方法的繼承,總是指向父類的prototype屬性。

[javascript] view plain copy
  1. console.log(ExtendStu.__proto__ === ExtendStuParent);//true  
  2. console.log(ExtendStu.prototype.__proto__ === ExtendStuParent.prototype);//true  
  3. //繼承的原理  
  4. // ExtendStu的實(shí)例繼承ExtendStuParent的實(shí)例  
  5. Object.setPrototypeOf(ExtendStu.prototype, ExtendStuParent.prototype);  
  6. // ExtendStu的實(shí)例繼承ExtendStuParent的靜態(tài)屬性  
  7. Object.setPrototypeOf(ExtendStu, ExtendStuParent);  
作為一個(gè)對(duì)象,子類(ExtendStu)的原型(__proto__屬性)是父類(ExtendStuParent);作為一個(gè)構(gòu)造函數(shù),子類(ExtendStu)的原型(prototype屬性)是父類的實(shí)例。
[javascript] view plain copy
  1. console.log(ExtendStuParent.__proto__ === Function.prototype);//true  
  2. console.log(ExtendStuParent.prototype.__proto__ === Object.prototype);//true  
ExtendStuParent作為一個(gè)基類(即不存在任何繼承),就是一個(gè)普通函數(shù),所以直接繼承Funciton.prototype。但是,ExtendStuParent調(diào)用后返回一個(gè)空對(duì)象(即Object實(shí)例),所以ExtendStuParent.prototype.__proto__指向構(gòu)造函數(shù)(Object)的prototype屬性。
[javascript] view plain copy
  1. console.log(Object.getPrototypeOf(ExtendStu) === ExtendStuParent  );//true  
Object.getPrototypeOf方法可以用來(lái)從子類上獲取父類。因此,可以使用這個(gè)方法判斷,一個(gè)類是否繼承了另一個(gè)類。

3.實(shí)例的__proto__屬性
子類實(shí)例的__proto__屬性的__proto__屬性,指向父類實(shí)例的__proto__屬性。也就是說(shuō),子類的原型的原型,是父類的原型。

[javascript] view plain copy
  1. class A{}  
  2. class B extends A{}  
  3. let a = new A();  
  4. let b = new B();  
  5. console.log(b.__proto__ === a.__proto__);//false  
  6. console.log(b.__proto__.__proto__ === a.__proto__);//true  
因此,通過(guò)子類實(shí)例的__proto__.__proto__屬性,可以修改父類實(shí)例的行為。
[javascript] view plain copy
  1. b.__proto__.__proto__.getName = ()=>{  
  2.     console.log('給父類添加一個(gè)函數(shù)');  
  3. };  
  4. b.getName();//給父類添加一個(gè)函數(shù)  
  5. a.getName();//給父類添加一個(gè)函數(shù)  
說(shuō)明:使用子類的實(shí)例給父類添加getName方法,由于B繼承了A,所以B和A中都添加了getName方法。

4.原生構(gòu)造函數(shù)的繼承
原生構(gòu)造函數(shù)是指語(yǔ)言內(nèi)置的構(gòu)造函數(shù),通常用來(lái)生成數(shù)據(jù)結(jié)構(gòu)。ECMAScript的原生構(gòu)造函數(shù)大致有下面這些。
Boolean()
Number()
String()
Array()
Date()
Function()
RegExp()
Error()
Object()

以前,這些原生構(gòu)造函數(shù)是無(wú)法繼承的,比如,不能自己定義一個(gè)Array的子類。

[javascript] view plain copy
  1. function MyArray() {  
  2.   Array.apply(this, arguments);  
  3. }  
  4.   
  5. MyArray.prototype = Object.create(Array.prototype, {  
  6.   constructor: {  
  7.     value: MyArray,  
  8.     writable: true,  
  9.     configurable: true,  
  10.     enumerable: true  
  11.   }  
  12. });  
上面代碼定義了一個(gè)繼承Array的MyArray類。但是,這個(gè)類的行為與Array完全不一致。
[javascript] view plain copy
  1. var colors = new MyArray();  
  2. colors[0] = "red";  
  3. colors.length  // 0  
  4.   
  5. colors.length = 0;  
  6. colors[0]  // "red"  
之所以會(huì)發(fā)生這種情況,是因?yàn)樽宇悷o(wú)法獲得原生構(gòu)造函數(shù)的內(nèi)部屬性,通過(guò)Array.apply()或者分配給原型對(duì)象都不行。原生構(gòu)造函數(shù)會(huì)忽略apply方法傳入的this,也就是說(shuō),原生構(gòu)造函數(shù)的this無(wú)法綁定,導(dǎo)致拿不到內(nèi)部屬性。ES5是先新建子類的實(shí)例對(duì)象this,再將父類的屬性添加到子類上,由于父類的內(nèi)部屬性無(wú)法獲取,導(dǎo)致無(wú)法繼承原生的構(gòu)造函數(shù)。比如,Array構(gòu)造函數(shù)有一個(gè)內(nèi)部屬性[[DefineOwnProperty]],用來(lái)定義新屬性時(shí),更新length屬性,這個(gè)內(nèi)部屬性無(wú)法在子類獲取,導(dǎo)致子類的length屬性行為不正常。
ES6允許繼承原生構(gòu)造函數(shù)定義子類,因?yàn)镋S6是先新建父類的實(shí)例對(duì)象this,然后再用子類的構(gòu)造函數(shù)修飾this,使得父類的所有行為都可以繼承。下面是一個(gè)繼承Array的例子。

[javascript] view plain copy
  1. //ExtendsArray.js  
  2. class ExtendsArray extends Array{}  
  3.   
  4. let extendsArray = new ExtendsArray();  
  5. console.log("=====ExtendsArray=====");  
  6. extendsArray[0] = '數(shù)據(jù)1';  
  7. console.log(extendsArray.length);  
上面代碼定義了一個(gè)ExtendsArray類,繼承了Array構(gòu)造函數(shù),因此就可以從ExtendsArray生成數(shù)組的實(shí)例。這意味著,ES6可以自定義原生數(shù)據(jù)結(jié)構(gòu)(比如Array、String等)的子類,這是ES5無(wú)法做到的。上面這個(gè)例子也說(shuō)明,extends關(guān)鍵字不僅可以用來(lái)繼承類,還可以用來(lái)繼承原生的構(gòu)造函數(shù)。因此可以在原生數(shù)據(jù)結(jié)構(gòu)的基礎(chǔ)上,定義自己的數(shù)據(jù)結(jié)構(gòu)。

5.Class的取值函數(shù)(getter)和存值函數(shù)(setter)
取值函數(shù)(getter)和存值函數(shù)(setter)可以自定義賦值和取值行為。當(dāng)一個(gè)屬性只有g(shù)etter沒(méi)有setter的時(shí)候,我們是無(wú)法進(jìn)行賦值操作的,第一次初始化也不行。如果把變量定義為私有的(定義在類的外面),就可以只使用getter不使用setter。

[javascript] view plain copy
  1. //GetSet.js  
  2. let data = {};  
  3.   
  4. class GetSet{  
  5.   
  6.     // 構(gòu)造  
  7.     constructor() {  
  8.         this.width = 10;  
  9.         this.height = 20;  
  10.     }  
  11.   
  12.     get width(){  
  13.         console.log('獲取寬度');  
  14.         return this._width;  
  15.     }  
  16.   
  17.     set width(width){  
  18.         console.log('設(shè)置寬度');  
  19.         this._width = width;  
  20.     }  
  21.     //當(dāng)一個(gè)屬性只有g(shù)etter沒(méi)有setter的時(shí)候,我們是無(wú)法進(jìn)行賦值操作的,第一次初始化也不行。  
  22.     //bundle.js:8631 Uncaught TypeError: Cannot set property width of #<GetSet> which has only a getter  
  23.   
  24.   
  25.     get data(){  
  26.         return data;  
  27.     }  
  28.     //如果把變量定義為私有的,就可以只使用getter不使用setter。  
  29.   
  30. }  
  31.   
  32. let getSet = new GetSet();  
  33. console.log("=====GetSet=====");  
  34. console.log(getSet.width);  
  35. getSet.width = 100;  
  36. console.log(getSet.width);  
  37. console.log(getSet._width);  
  38. console.log(getSet.data);  
  39.   
  40. //存值函數(shù)和取值函數(shù)是設(shè)置在屬性的descriptor對(duì)象上的。  
  41. var descriptor = Object.getOwnPropertyDescriptor(  
  42.     GetSet.prototype, "width");  
  43. console.log("get" in descriptor);//true  
  44. console.log("set" in descriptor);//true  
如果把上面的get和set改成以下形式,不加下劃線報(bào)Uncaught RangeError: Maximum call stack size exceeded錯(cuò)誤。這是因?yàn)?在構(gòu)造函數(shù)中執(zhí)行this.name=name的時(shí)候,就會(huì)去調(diào)用set name,在set name方法中,我們又執(zhí)行this.name = name,進(jìn)行無(wú)限遞歸,最后導(dǎo)致棧溢出(RangeError)。
[javascript] view plain copy
  1. get width(){  
  2.     console.log('獲取寬度');  
  3.     return this.width;  
  4. }  
  5.   
  6. set width(width){  
  7.     console.log('設(shè)置寬度');  
  8.     this.width = width;  
  9. }  
說(shuō)明:以上width的getter和setter只是給width自定義存取值行為,開發(fā)者還是可以通過(guò)_width繞過(guò)getter和setter獲取width的值。

6.new.target屬性
new是從構(gòu)造函數(shù)生成實(shí)例的命令。ES6為new命令引入了一個(gè)new.target屬性,(在構(gòu)造函數(shù)中)返回new命令作用于的那個(gè)構(gòu)造函數(shù)。如果構(gòu)造函數(shù)不是通過(guò)new命令調(diào)用的,new.target會(huì)返回undefined,因此這個(gè)屬性可以用來(lái)確定構(gòu)造函數(shù)是怎么調(diào)用的。

[javascript] view plain copy
  1. class Targettu{  
  2.     // 構(gòu)造  
  3.     constructor() {  
  4.         if(new.target !== undefined){  
  5.             this.height = 10;  
  6.         }else{  
  7.             throw new Error('必須通過(guò)new命令創(chuàng)建對(duì)象');  
  8.         }  
  9.     }  
  10. }  
  11. //或者(判斷是不是通過(guò)new命令創(chuàng)建的對(duì)象下面這種方式也是可以的)  
  12. class Targettu{  
  13.     // 構(gòu)造  
  14.     constructor() {  
  15.         if(new.target === Targettu){  
  16.             this.height = 10;  
  17.         }else{  
  18.             throw new Error('必須通過(guò)new命令創(chuàng)建對(duì)象');  
  19.         }  
  20.     }  
  21. }  
  22.   
  23. let targettu = new Targettu();  
  24. //Uncaught Error: 必須通過(guò)new命令創(chuàng)建對(duì)象  
  25. //let targettuOne = Targettu.call(targettu);  
需要注意的是,子類繼承父類時(shí),new.target會(huì)返回子類。通過(guò)這個(gè)原理,可以寫出不能獨(dú)立使用、必須繼承后才能使用的類。
[javascript] view plain copy
  1. // 構(gòu)造  
  2. constructor(name,age) {  
  3.     //用于寫不能實(shí)例化的父類  
  4.     if (new.target === ExtendStuParent) {  
  5.         throw new Error('ExtendStuParent不能實(shí)例化,只能繼承使用。');  
  6.     }  
  7.     this.name = name;  
  8.     this.age = age;  
  9. }  

7.Mixin模式的實(shí)現(xiàn)
Mixin模式指的是,將多個(gè)類的接口“混入”(mix in)另一個(gè)類。它在ES6的實(shí)現(xiàn)如下。

[javascript] view plain copy
  1. //MixinStu.js  
  2. export default function mix(...mixins) {  
  3.     class Mix {}  
  4.   
  5.     for (let mixin of mixins) {  
  6.         copyProperties(Mix, mixin);  
  7.         copyProperties(Mix.prototype, mixin.prototype);  
  8.     }  
  9.   
  10.     return Mix;  
  11. }  
  12.   
  13. function copyProperties(target, source) {  
  14.     for (let key of Reflect.ownKeys(source)) {  
  15.         if ( key !== "constructor"  
  16.             && key !== "prototype"  
  17.             && key !== "name"  
  18.         ) {  
  19.             let desc = Object.getOwnPropertyDescriptor(source, key);  
  20.             Object.defineProperty(target, key, desc);  
  21.         }  
  22.     }  
  23. }  
上面代碼的mix函數(shù),可以將多個(gè)對(duì)象合成為一個(gè)類。使用的時(shí)候,只要繼承這個(gè)類即可。
[javascript] view plain copy
  1. class MixinAll extends MixinStu(B,Serializable){  
  2.   
  3.     // 構(gòu)造  
  4.     constructor(x,y,z) {  
  5.         super(x,y);  
  6.         this.z = z;  
  7.     }  
  8.   
  9. }  
更多的資料:點(diǎn)擊打開鏈接   點(diǎn)擊打開鏈接

Class的繼承(extends)和自定義存值(setter)取值(getter)的整個(gè)案例:
[javascript] view plain copy
  1. //ExtendStuParent.js  
  2. const ExtendStuParent = class ExtendStuParent{  
  3.   
  4.     name;//定義name  
  5.     age;//定義age  
  6.   
  7.     // 構(gòu)造  
  8.     constructor(name,age) {  
  9.         //用于寫不能實(shí)例化的父類  
  10.         if (new.target === ExtendStuParent) {  
  11.             throw new Error('ExtendStuParent不能實(shí)例化,只能繼承使用。');  
  12.         }  
  13.         this.name = name;  
  14.         this.age = age;  
  15.     }  
  16.   
  17.     getName(){  
  18.         return this.name;  
  19.     }  
  20.   
  21.     getAge(){  
  22.         return this.age;  
  23.     }  
  24. };  
  25.   
  26. ExtendStuParent.prototype.width = '30cm';  
  27.   
  28. export default ExtendStuParent;  
  29.   
  30. //ExtendStu.js  
  31. import ExtendStuParent from './ExtendStuParent';  
  32.   
  33. export default class ExtendStu extends ExtendStuParent{  
  34.   
  35.     // 構(gòu)造  
  36.     constructor(name, age, height) {  
  37.         super(name,age);  
  38.         this.height = height;  
  39.         //父類和子類都有實(shí)例屬性name,但是在toString方法調(diào)用getName的時(shí)候,  
  40.         //返回的是子類的的數(shù)據(jù)  
  41.         this.name = 'LiSi 子類';  
  42.   
  43.         //  
  44.         this.color = 'black';  
  45.         super.color = 'white';  
  46.         console.log(super.color);//undefined  
  47.         console.log(this.color);//black  
  48.     }  
  49.   
  50.     toString(){  
  51.         console.log(super.age);//undefined  
  52.         console.log(super.width);//30cm  
  53.         return "name:"+ super.getName() + " age:"+this.age + " height:"+this.height;  
  54.     }  
  55.   
  56. }  

參考資料:點(diǎn)擊打開鏈接

ES6 類(Class)的繼承(extends)和自定義存(setter)取值(getter)詳解

博客地址:http://blog.csdn.net/pcaxb/article/details/53784309

下載地址:http://download.csdn.net/detail/pcaxb/9717587




    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    最近日韩在线免费黄片| 亚洲一区二区三区熟女少妇| 亚洲欧美日本国产不卡| 天堂网中文字幕在线观看| 久久国产精品熟女一区二区三区| 韩日黄片在线免费观看| 欧美一级黄片免费视频| 亚洲黑人精品一区二区欧美| 亚洲午夜福利视频在线| 国产亚州欧美一区二区| 色婷婷人妻av毛片一区二区三区| 大尺度剧情国产在线视频| 98精品永久免费视频| 国产内射在线激情一区| 久久精品亚洲精品国产欧美| 激情偷拍一区二区三区视频| 日韩中文字幕人妻精品| 亚洲二区欧美一区二区| 99久久精品一区二区国产| 久久99青青精品免费观看| 暴力三级a特黄在线观看| 亚洲中文字幕日韩在线| 国产高清在线不卡一区| 老司机激情五月天在线不卡| 久久成人国产欧美精品一区二区| 久久大香蕉一区二区三区| 国产免费观看一区二区| 欧美精品在线观看国产| 深夜日本福利在线观看| 中文字幕人妻av不卡| 国产又粗又硬又长又爽的剧情| 亚洲精品中文字幕一二三| 九九热精品视频免费观看| 国产高清一区二区不卡| 久久亚洲午夜精品毛片| 欧美一级特黄特色大色大片| 日韩一区二区三区在线欧洲| 亚洲精品一区二区三区免 | 在线视频免费看你懂的| 欧美老太太性生活大片| 人妻久久这里只有精品|