Contents
  1. 1. ES5的类
    1. 1.1. 类的继承
      1. 1.1.1. 面向对象继承
      2. 1.1.2. 原型继承
      3. 1.1.3. 惨元类
  2. 2. ES6的类
  3. 3. ES6继承

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继承

Contents
  1. 1. ES5的类
    1. 1.1. 类的继承
      1. 1.1.1. 面向对象继承
      2. 1.1.2. 原型继承
      3. 1.1.3. 惨元类
  2. 2. ES6的类
  3. 3. ES6继承