JS语言传统方法通过构造函数定义并生成新对象,ES6引入了Class这个概念作为对象的模板,通过class关键字可以定义类。

基本语法

function Point(x, y) {  this.x = x;  this.y = y;}Point.prototype.toString = function () {  return '(' + this.x + ',' + this.y + ')';}Point.protortype.doStuff = function () {  console.log('stuff');}// 等同于class Point {  constructor(x, y) {    this.x = x;    this.y = y;  }  toString() {    return '(' + this.x + ',' + this.y + ')';  }  doStuff() {    console.log('stuff');  }}typeof Point // "function"Point === Point.prototype.constructor // true

上面的代码表明,类的数据类型就是函数,类本身指向构造函数。类的所有方法都定义在类的prototype属性上。在类的实例上调用方法,就是在调用原型上的方法。constructor方法是类的默认方法,通过new命令生成对象实例时自动调用该方法。如果没有显示定义,一个空的construtor方法会被默认添加。

类的实例对象

生成实例对象的写法和ES5一样,也是使用new命令,不同于ES5,如果忘记加new将会报错。

class Point {}// 等同于class Point {  constructor() {  }}var b = new Point(2, 3); // 生成Point实例var c = Pint(2, 3) // 报错

Class表达式

与函数一样,Class可以使用表达式的形式定义。

const MyClass = class Me {   getClassName() {    return Me.Name;  }}

需要注意的是这个类的名字是MyClass,而不是Me,Me只在Class内部有定义指代当前类。如果Class内部没有用到,那么可以省略Me。

let int = new MyClass();int.getClassName() // MeMe.Name // 报错const MyClass = class {   getClassName() {    return Me.Name;  }}# 不存在变量提升类不存在变量提升,与ES5完全不同。

new Foo() // 报错
class Foo {}

# Class的实例属性Class的实例属性可以用等式写入类的定义中。

class MyClass {
myProp = 42;

constructor() {

console.log(this.myProp); // 42

}
}

new.target属性

ES6引入了new.target属性,(在构造函数中)返回new命令所作用的构造函数。如果构造函数不是通过new命令调用,那么new.target将会返回undefined,可以利用这个属性确定构造函数是怎么调用的。

function Person(name) {  if (new.target !== undefined) {    this.name = name;  } else {    throw new Error('必须使用new生成实例')  }  /*另一种写法  if (new.target == Person) {    this.name = name;  } else {    throw new Error('必须使用new生成实例')  }*/   }var Person = new Person('张三'); // 正确var notAPerson = Person.call(person, '张三'); //报错

Class内部调用new.target,返回当前Class。

class Rectangle {  constructor(length, width) {    console.log(new.target === Rectangle);    this.length = length;    this.width = width;  }}var obj = new Rectangle(3, 4); // 输出true

值得注意的是,之类基础父类时new.target会返回之类。利用这个特点,可以写出不能独立使用而是必须继承后才能使用的类。

class Shape {  constructor() {    if (new.target === Shape) {      throw new Error('本类不能实例化');    }  }  }class Rectangle extends Shape {  constructor(length, width) {    super();    //...  }  }var x = new Shape(); // 报错var y = new Rectangle(); // 正确

Class的继承

Class可以通过extends关键字实现继承,之类必须在constructor方法中调用super方法,否则新建实例会报错。这是因为子类没有自己的this对象,而是继承父类的this对象。如果子类没有定义constructor方法,那么constructor方法(调用super)会被默认添加。

class Point { /* ... */}class ColorPoint extends Point {  constructor() {    //此处应调用super();  }}let cp = new ColorPoint(); // 报错

另一个注意点是,在子类的构造函数中只有在调用super之后才可以使用this关键字,否则会报错。

class Point {  constructor(x, y) {    this.x = x;    this.y = y;  }  }class ColorPoint extends Point {  constructor(x, y , color) {    this.color = color; //报错    super(x, y);    this.color = color; //正确  }}

super关键字

用作函数

super既可以当作函数使用,也可以当作对象使用。第一种情况,super作为函数调用时代表父类的构造函数。但是返回的之类的实例,即super内部的this指向实例。因此super()相当于A.prototype.constructor.call(this)。

class A {  constructor() {    console.log(new.target.name);  }}class B extends A {  constructor() {    super();  //作为函数时只能在子类的构造函数中  }}new A // Anew B // B

作为对象

第二种情况,super作为对象时在在普通方法中指向父类的原型对象;在静态方法中指向父类。

class A {  p() {    return 2;  }  }class B extends A {  constructor() {    super();    console.log(super.p()); // 2 这里相当于A.prototype.p()  }}let b = new B(); 

this指向

ES6规定,通过super调用父类的方法时,super会绑定子类的this。如果通过super对某个属性赋值,这时的super就是this,赋值的属性会变成子类实例的属性。

class A {  constructor() {    this.x = 1;  }  print() {    console.log(this.x);  }}class B extends A {  constructor() {    super();    this.x = 2;    super.x = 3;  }  m() {    super.print();  }  }let b = new B();b.m() // 3

ps ...参考资料《ES6标准入门》(第三版)