Class

class

基本语法

ES6引入Class这个概念,作为对象模板。

//es5
function PersonType(name) {
    this.name = name;
}

PersonType.prototype.sayName = function() {
    console.log(this.name);
};

let person = new PersonType("Nicholas");
person.sayName();   // 输出 "Nicholas"

//ES6

class PersonClass {

    // 等价于 PersonType 构造器
    constructor(name) {
        this.name = name;
    }

    // 等价于 PersonType.prototype.sayName
    sayName() {
        console.log(this.name);
    }
}
let person = new PersonClass("Nicholas");
person.sayName();   // 输出 "Nicholas"

ES5的构造函数Point,对应ES6的Point类的构造方法。

构造函数的prototype属性,在ES6的“类”上面继续存在。

class Point {
    constructor(){
    // ...
    }

    toString(){
    // ...
    }

    toValue(){
    // ...
    }
}

// 等同于

Point.prototype = {
  toString(){},
  toValue(){}
};

prototype对象的constructor属性,直接指向“类”的本身。

Point.prototype.constructor === Point // true

constructor方法

constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。

constructor方法默认返回实例对象(即this),完全可以指定返回另外一个对象。

class Foo {
  constructor() {
    return Object.create(null);
  }
}

new Foo() instanceof Foo
// false

类的实例对象

类的调用的要加上new,类的所有实例共享一个原型对象。

var p1 = new Point(2,3);
var p2 = new Point(3,2);

p1.__proto__.printName = function () { return 'Oops' };

p1.printName() // "Oops"
p2.printName() // "Oops"

var p3 = new Point(4,2);
p3.printName() // "Oops"

this的指向

类的方法内部如果含有this,它默认指向类的实例。

class Logger {
  printName(name = 'there') {
    this.print(`Hello ${name}`);
  }

  print(text) {
    console.log(text);
  }
}
const logger = new Logger();
logger.printName();
//Hello there

Class的继承

Class之间可以通过extends关键字实现继承。

class Point {
    constructor(x,y,color){
        this.x=x;
        this.y=y;
        this.color=color;
    }
    chooseColor(){
        console.log('color:',this.color);
    }
}

class ColorPoint extends Point {
  constructor() {
      super(1,2,'blue');
  }
  toString(){
      return 'ColorPoint:'+super.color
  }
}

var cp=new ColorPoint();
cp.chooseColor()
//VM2425:8 color: blue

super表示父类的构造函数,新建父类的this对象。子类没有调用,会报错。

类的prototype属性和__proto__属性

  1. 子类的__proto__属性,表示构造函数的继承,总是指向父类。

  2. 子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。

class A {
}

class B extends A {
}

B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true

Extends 的继承目标

子类继承Object类。

class A extends Object {
}

A.__proto__ === Object // true
A.prototype.__proto__ === Object.prototype // true

A其实就是构造函数Object的复制,A的实例就是Object的实例。

不存在任何继承。

class A {
}

A.__proto__ === Function.prototype // true
A.prototype.__proto__ === Object.prototype // true

这种情况下,A作为一个基类(即不存在任何继承),就是一个普通函数,所以直接继承Funciton.prototype。调用后返回空对象,指向造函数(Object)的prototype属性。

子类继承null。

class A extends null {
}

A.__proto__ === Function.prototype // true
A.prototype.__proto__ === undefined // true

super

super代表父类A的构造函数,返回子类b的实例。 spuer内部this指向B

class A {
  constructor() {
    console.log(new.target.name);
  }
}
class B extends A {
  constructor() {
    super();
  }
}
new A() // A
new B() // B

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

通过super调用父类的方法时,super会绑定子类的this

class A {
  constructor() {
    this.x = 1;
  }
  print() {
    console.log(this.x);
  }
}

class B extends A {
  constructor() {
    super();
    this.x = 2;
  }
  m() {
    super.print();
    //new A().print.call(this);
  }
}

let b = new B();
b.m() // 2

实例的__proto__属性

子类实例的__proto__属性的__proto__属性,指向父类实例的__proto__属性。也就是说,子类的原型的原型,是父类的原型。

var p1 = new Point(2, 3);
var p2 = new ColorPoint(2, 3, 'red');

p2.__proto__ === p1.__proto__ // false
p2.__proto__.__proto__ === p1.__proto__ // true

Class的取值函数(getter)和存值函数(setter)

class MyClass {
  constructor() {
    // ...
  }
  get prop() {
    return 'getter';
  }
  set prop(value) {
    console.log('setter: '+value);
  }
}

let inst = new MyClass();

inst.prop = 123;
// setter: 123

inst.prop
// 'getter'

prop属性有对应的存值函数和取值函数,因此赋值和读取行为都被自定义了。

Class 的 Generator 方法

class Foo {
  constructor(...args) {
    this.args = args;
  }
  * [Symbol.iterator]() {
    for (let arg of this.args) {
      yield arg;
    }
  }
}

for (let x of new Foo('hello', 'world')) {
  console.log(x);
}
// hello
// world

Foo类的Symbol.iterator方法前有一个星号,表示该方法是一个 Generator 函数。Symbol.iterator方法返回一个Foo类的默认遍历器

Class 的静态方法

如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用。父类的静态方法,能被子类继承。也可以在super上调用。

class Foo {
  static classMethod() {
    return 'hello';
  }
}

Foo.classMethod() // 'hello'

var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function

ES6只用静态方法,没有静态属性。

new.target属性

new命令作用于的那个构造函数,子类继承父类时,new.target会返回子类。

class Rectangle {
  constructor(length, width) {
    console.log(new.target === Rectangle);
    this.length = length;
    this.width = width;
  }
}

var obj = new Rectangle(3, 4); // 输出 true

Mixin模式的实现

Mixin模式指的是,将多个类的接口“混入”(mix in)另一个类。

function mix(...mixins) {
  class Mix {}

  for (let mixin of mixins) {
    copyProperties(Mix, mixin);
    copyProperties(Mix.prototype, mixin.prototype);
  }

  return Mix;
}

function copyProperties(target, source) {
  for (let key of Reflect.ownKeys(source)) {
    if ( key !== "constructor"
      && key !== "prototype"
      && key !== "name"
    ) {
      let desc = Object.getOwnPropertyDescriptor(source, key);
      Object.defineProperty(target, key, desc);
    }
  }
}

class DistributedEdit extends mix(Loggable, Serializable) {
  // ...
}

上面代码的mix函数,可以将多个对象合成为一个类。

Last updated