Classes

ECMAScript 6 引入了 JavaScript 类,给原有的JavaScript原型继承提供了语法糖. 类的语法并没有向JavaScript引进一个新的面向对象的继承模式. classes为创建对象,处理继承提供了简单,清晰的语法.

定义类

类其实是”特殊的函数”.就像你可以定义 表达式函数 function expressions声明式函数 function declaration 一样,类也有两种形式 表达式类声明式类

声明式类

定义类的方式之一就是声明式类. 要声明一个类,可以使用 class 关键字,加上类的名字(在这里叫”Ploygon”).

class Polygon {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

预声明

函数声明和类声明的一个重要区别在于,函数会预声明,而类不会.你必须先声明类,然后再使用它.像下面这样的代码会报错: ReferenceError.

var p = new Polygon(); // ReferenceError

class Polygon {}

表达式类

表达式类是定义类的另一种方式. 表达式类可以有名字也没有没有名字.给一个已经有名字的表达式类一个变量名,两个名字必须相等…(正常人都不会这样玩吧…)

// unnamed
var Polygon = class {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};

// named
var Polygon = class Polygon {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};

类体和方法的定义

{}里的部分就被成为类体.这是你定义类内容的地方,比如类的方法和构造器.

严格模式

类体(无论是声明式类还是表达式类)里的代码,都是以严格模式执行的.

Constructor

constructor是一个特殊的方法,用于从所在的 class 创建和实例化对象.在一个类里,只能有一个名叫 “constructor” 的特殊方法.如果 constructor 方法在一个类里出现一次以上,会报错: SyntaxError

constructor可以使用 super 关键字来调用父类的constructor

原型方法

参考 方法定义(method definitions).

class Polygon {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }

  get area() {
    return this.calcArea();
  }

  calcArea() {
    return this.height * this.width;
  }
}

静态方法

static 关键字给类定义了静态方法.静态方法的调用不需要实例化类,并且实例化以后的实例不能调用静态方法.静态方法一般用于给应用提供工具函数.

class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }

    static distance(a, b) {
        const dx = a.x - b.x;
        const dy = a.y - b.y;

        return Math.sqrt(dx*dx + dy*dy);
    }
}

const p1 = new Point(5, 5);
const p2 = new Point(10, 10);

console.log(Point.distance(p1, p2));

通过 extends 定义子类

extends关键字在声明式类和表达式类里都可以用,用于创建一个类的子类.

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.');
  }
}

通过 super 调用超类

super 关键字可以调用父类的方法

class Cat { 
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(this.name + ' makes a noise.');
  }
}

class Lion extends Cat {
  speak() {
    super.speak();
    console.log(this.name + ' roars.');
  }
}

混合(Mix-ins)

抽象子类或混合,是类的模板.ECMAScript类只能拥有一个超类,所以,继承多个工具类是不可行的. 所有的工具函数都必须又超类提供.

想要在 ECMAScript 里实现混合,可以创造一个函数,它接受一个超类作为输入参数,然后输出一个继承该超类的子类:

var CalculatorMixin = Base => class extends Base {
  calc() { }
};

var RandomizerMixin = Base => class extends Base {
  randomize() { }
};

一个使用了这些混合的类看起来应该是这样的:

class Foo { }
class Bar extends CalculatorMixin(RandomizerMixin(Foo)) { }

这个可能比较难理解,因为我把以上代码转换成es5以后,发现代码非常多,非常长…所以还是先从概念上去理解…
首先,我把这个例子写的更深入一点:

"use strict";
var CalculatorMixin = Base => class extends Base {
  calc() { 
    return 'calcResult';
  }
};

var RandomizerMixin = Base => class extends Base {
  randomize() { 
    return 'randomizeResult'
  }
};

var Bunny = Base => class extends Base {
  bunny() { 
    return 'bunny'
  }
};

class Foo { }
class Bar extends CalculatorMixin(RandomizerMixin(Bunny(Foo))) { }

var bar = new Bar();
console.log(bar.calc());       //'calcResult'
console.log(bar.randomize());  //'dandomizeResult'
console.log(bar.bunny());      //'bunny'

通过这个例子,可以很好的明白,ECMAScript的”混合”是在做什么: 它可以让一个类去包含多个”混合”里的工具函数,然后实例化这个类的时候,实例也会继承来自多个”混合”的方法.

原文地址