ES5的类
看完类的继承这一章,我不禁思考我之前类的应用环境是否正确,同时真正做类的继承的时候,需要好好思考。抽象能力真的很重要。
类的继承
类的继承有面向对象式的继承和原型继承。
面向对象继承
1 2 3 4 5 6 7 8
| function extend(subClass,superClass){ var F=function(){}; F.prototype=superClass.prototype; subClass.prototype=new F(); //不直接复制,是因为引用,防止改变父类的值 subClass.prototype.constructor=subClass; //维持原型链 }
|
这里只复制了原型但是属性还没有复制,可以把类当函数对待,用call调用到子类上就可以了。superClass.call(subClass,[args])。
然后可以改变一下extend函数,就可以访问父类的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13
| function extend(subClass,superClass){ var F=function(){}; F.prototype=superClass.prototype; subClass.prototype=new F(); //不直接复制,是因为引用,防止改变父类的值 subClass.prototype.constructor=subClass; //维持原型链 subClass.superClass=superClass.prototype; if(superClass.prototype.constructor==Object.prototype.constructor){ superClass.prototype.constructor=superClass; } }
|
这里弱耦合了父类,这样可以直接subClass.superClass访问父类的方法属性,但是在这里重载不能很好的弱耦合的调用父类的方法。后面可以介绍一种更好的方法。
原型继承
其实跟原型继承跟类的继承差不多,其实这里可以直接这样:
var subClass.prototype=new superClass(),这样这里的子类的原型就等于父类的实例,但是这里有一点要注意,这样也会复制父类的属性,如果子类改变父类的某个属性可能会影响所有实例。所以子类要重新申明一下有的变量。
惨元类
这是《JavaScript设计模式》中的翻译,大概意思就是重新建立一个类,然后用augment函数附到相应的类中,这样的好处可以减少代码的难度。
下面是augment:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function augment(receivingClass,givingClass){ if(arguments.length>2){ for(var i=2,len=arguments.length;i<len;i++){ receivingClass.prototype[arguments[i]]=givingClass.prototype[arguments[i]]; } }else{//give all methods for(methodName in givingClass.prototype){ if(!receivingClass.prototype[methodName]){ receivingClass.prototype[methodName]=givingClass.prototype[methodName]; } } } }
|
TODO#### 方便重载访问父类的方法
ES6的类
不用像函数那样定义类了,可以像下面这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| //定义类 class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } } //等于 class Point { // ... } typeof Point // "function" Point === Point.prototype.constructor // true //---------------------- class Point { constructor(){ // ... } toString(){ // ... } toValue(){ // ... } } // 等同于 Point.prototype = { toString(){}, toValue(){} };
|
由于类的方法都定义在prototype对象上面,所以类的新方法可以添加在prototype对象上面。Object.assign方法可以很方便地一次向类添加多个方法。
1 2 3 4 5 6 7 8 9 10
| class Point { constructor(){ // ... } } Object.assign(Point.prototype, { toString(){}, toValue(){} });
|
另外,类的内部所有定义的方法,都是不可枚举的(non-enumerable)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Point { constructor(x, y) { // ... } toString() { // ... } } Object.keys(Point.prototype) // [] Object.getOwnPropertyNames(Point.prototype) // ["constructor","toString"]
|
TODO:ES6内部私有变量
this的指向:
类的方法内部如果含有this,它默认指向类的实例。但是,必须非常小心,一旦单独使用该方法,很可能报错。
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Logger { printName(name = 'there') { this.print(`Hello ${name}`); } print(text) { console.log(text); } } const logger = new Logger(); const { printName } = logger; printName(); // TypeError: Cannot read property 'print' of undefined
|
最好用箭头函数或bind
1 2 3 4 5 6 7
| class Logger { constructor() { this.printName = this.printName.bind(this); } // ... }
|
1 2 3 4 5 6 7 8 9
| class Logger { constructor() { this.printName = (name = 'there') => { this.print(`Hello ${name}`); }; } // ... }
|
ES6继承
1
| class ColorPoint extends Point {}
|
ES6继承