ES6 的 Class

目录

一、类的语法

1、类的特性

(1)、class 与 function 相同点

(2)、class 与 function 不同点

2、类 表达式

3、类里的 取值函数(getter)和存值函数(setter)

4、类中 this 的指向

二、类的属性——name

三、类的方法

1、constructor 方法

四、类的静态属性和静态方法—— static

1、类的静态属性

2、类的静态方法

五、类的私有属性和私有方法

1、类的私有属性

2、类的私有方法

六、类的实例

1、创建类的实例

2、实例属性的新写法

3、通过实例为类添加方法

七、类的继承

1、extends 关键字

2、 super 关键字

(1)、super 关键字作为函数

(2)、super 关键字作为对象

(3)、使用 super 对象的注意事项

3、ES6 允许原生构造函数被继承

4、静态方法也可以被继承

5、判断一个类是否继承了另一个类

6、类的 prototype 属性和__proto__属性 

7、Mixin 模式的实现

八、class 的使用

1、 ES6 与 ES7 的 class 的语法差异


ES6 引入了 Class(类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。

一、类的语法

1、类的特性

class 是 function 的语法糖。

class Point {
  // ...
}

typeof Point // "function"
Point === Point.prototype.constructor // true

上面代码表明,类的数据类型就是函数,类本身就指向构造函数。

(1)、class 与 function 相同点

class 继承了 function 大部分的功能与特点,例如:

  • 创建 class 的实例的时候,直接对类使用 new 命令,跟构造函数的用法完全一致。
  • prototype 对象的 constructor 属性,直接指向“类”的本身。
  • 类的所有方法都定义在类的prototype 属性上面。
  • class 的新方法可以直接添加在 prototype 对象上面。

1⃣️、使用 class 的时候,直接对类使用 new 命令,跟构造函数的用法完全一致。

// 使用 new 命令创建类的实例
class Bar { /* ... */ }

var b = new Bar();

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

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

3⃣️、类的所有方法都定义在类的prototype 属性上面。

class Point {
  constructor() {
    // ...
  }
 
  toString() {
    // ...
  }
 
  toValue() {
    // ...
  }
}
 
// 等同于
 
Point.prototype = {
  constructor() {},
  toString() {},
  toValue() {},
};

在类的实例上面调用方法,其实就是调用原型上的方法。

class B {}
let b = new B();
 
b.constructor === B.prototype.constructor // true

上面代码中,b是B类的实例,它的constructor方法就是B类原型的constructor方法。

4⃣️、class 的新方法可以添加在 prototype 对象上面。

使用 Object.assign() 方法可以很方便地一次向类添加多个方法。

class Point {
  constructor(){
    // ...
  }
}
 
Object.assign(Point.prototype, {
  toString(){},
  toValue(){}
});

(2)、class 与 function 不同点

  • class 内部定义的所有方法,都是不可枚举的。
  • class 必须使用 new 调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。
  • class 内部与模块内部类似,默认就是严格模式,所以不需要使用 use strict 指定运行模式。
  • class 内部不存在变量提升。
  • 类的属性名,可以采用表达式。

1⃣️、class 内部定义的所有方法,都是不可枚举的。

// 采用 ES5 的 function 写法,可枚举
var Point = function (x, y) {
  // ...
};
 
Point.prototype.toString = function() {
  // ...
};
 
Object.keys(Point.prototype)
// ["toString"]
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]
 
 
// 采用 ES6 的 class 写法,不可枚举
class Point {
  constructor(x, y) {
    // ...
  }
 
  toString() {
    // ...
  }
}
 
Object.keys(Point.prototype)
// []
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]

2⃣️、class 必须使用 new 调用,否则会报错。

这是它跟普通构造函数的一个主要区别,后者不用 new 也可以执行。

class Foo {
  constructor() {
    return Object.create(null);
  }
}
 
Foo()
// TypeError: Class constructor Foo cannot be invoked without 'new'

3⃣️、class 内部不存在变量提升。

类不存在变量提升(hoist),这一点与 function 完全不同。

new Foo(); // ReferenceError
class Foo {}

4⃣️、类的属性名,可以采用表达式。

let methodName = 'getArea';
 
class Square {
  constructor(length) {
    // ...
  }
  [methodName]() {
    // ...
  }
}

上面代码中,Square类的方法名getArea,是从表达式得到的。

2、类 表达式

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

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

上面代码中,定义的类的名字是 Me,但是 Me 只在 Class 的内部可用,指代当前类。在 Class 外部,这个类只能用 MyClass 引用。

let inst = new MyClass();
inst.getClassName() // Me
Me.name // ReferenceError: Me is not defined

如果类的内部没用到的话,可以省略 Me,也就是可以写成下面的形式。

const MyClass = class { /* ... */ };

采用 Class 表达式,可以写出立即执行的 Class。

let person = new class {
  constructor(name) {
    this.name = name;
  }
 
  sayName() {
    console.log(this.name);
  }
}('张三');
 
person.sayName(); // "张三"

3、类里的 取值函数(getter)和存值函数(setter)

与 ES5 一样,在“类”的内部可以使用 get 和 set 关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。

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 属性有对应的存值函数和取值函数,因此赋值和读取行为都被自定义了。

getter 与 setter 必须同级出现,否则会报错。

class Example {
    constructor(a) {
        this.a = a; 
    }
    get a() {
        return this.a;
    }
}
let exam = new Example(1); // Uncaught TypeError: Cannot set property // a of #<Example> which has only a getter

4、类中 this 的指向

类的方法内部的 this 始终默认指向类的实例

将类中私有方法提取出来单独使用,可能会报错:

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

解决上述类似报错的方法有三种:

  • 在构造方法中绑定 this。
  • 使用箭头函数。(推荐)
  • 使用Proxy,获取方法的时候,自动绑定this。

1⃣️、在构造方法中绑定 this

class Logger {
  constructor() {
    this.printName = this.printName.bind(this);
  }
 
  // ...
}

2⃣️、使用箭头函数(推荐)

class Obj {
  constructor() {
    this.getThis = () => this;
  }
}
 
const myObj = new Obj();
myObj.getThis() === myObj // true

3⃣️、使用Proxy,获取方法的时候,自动绑定this

function selfish (target) {
  const cache = new WeakMap();
  const handler = {
    get (target, key) {
      const value = Reflect.get(target, key);
      if (typeof value !== 'function') {
        return value;
      }
      if (!cache.has(value)) {
        cache.set(value, value.bind(target));
      }
      return cache.get(value);
    }
  };
  const proxy = new Proxy(target, handler);
  return proxy;
}
 
const logger = selfish(new Logger());

二、类的属性——name

与ES5的普通构造函数一样,类也有一个 name 属性,用来返回紧跟在 class 关键字后面的类名。

class Point {}
Point.name // "Point"

三、类的方法

1、constructor 方法

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

ES6 规定:一个类必须有 constructor 方法,如果没有显式定义,一个空的 constructor 方法会被默认添加。

constructor 

即便是你调用 super 时没有传递 props 参数,仍然可以在 render 和其他方法中访问this.props。 (不信你可以亲自去试试!)

class Point {
}
 
// 等同于
class Point {
  constructor() {}
}

ES7 简化了此规定,将初始状态从构造函数中转变成属性初始化。

// ES6 
class Point {
  constructor() {
    this.state = {
        number: 123
    }
  }
}
 
// 等同于
// ES7
class Point {
  state = {
    number: 123
  }
}

四、类的静态属性和静态方法—— static

1、类的静态属性

静态属性指的是定义在 Class 本身的属性,而不是定义在实例对象(this)上的属性。

class Foo {}
Foo.prop = 1;

Foo.prop;// 1

ES6 明确规定,Class 内部只有静态方法,没有静态属性。

ES7 提供了类的静态属性,写法是在实例属性的前面加上 static 关键字。 

class MyClass {
  static myStaticProp = 42;
}

MyClass.myStaticProp;// 42

类的静态属性与实例属性的不同:静态属性只要定义在其类中就行,但是实例属性只能在 constructor 构造函数中被定义。

举个例子(顺便看看 “静态属性、实例属性、state 对象上的属性的使用区别”):

class OneClass {
    // 定义静态属性
    static str = 'qwer';

    // 定义实例属性(ES6 只能在 constructor 构造函数中定义实例属性)
    constructor() {
        this.width = '40cm';
        this.state = {
            age: 18
        };
    }

    getWidth(){
        return this.width;// 使用实例属性的时候需要加上 this
    }
}

var one = new OneClass();
one.width;// "40cm"
one.state.age;// 18

OneClass.str;// "qwer"

由上述代码可知:

  • 静态属性只能通过类本身调用。
  • 实例属性只能通过该类的实例调用。
  • state 对象上的属性只能通过 “该类的实例.state” 的方式调用。

2、类的静态方法

类(class)通过 static 关键字定义静态方法。

静态方法的特性:

  • 不能在类的实例上调用静态方法,而应该通过类本身调用。
  • 如果静态方法包含this关键字,这个this指的是类,而不是实例。
  • 父类的静态方法,可以被子类继承。
  • 静态方法也是可以从super对象上调用的。

①、不能在类的实例上调用静态方法,而应该通过类本身调用。

class Foo {
  static classMethod() {
    return 'hello';
  }
}
 
Foo.classMethod() // 'hello'
 
var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function

②、如果静态方法包含this关键字,这个this指的是类,而不是实例。

class Foo {
  static bar() {
    this.baz();
  }
  static baz() {
    console.log('hello');
  }
  baz() {
    console.log('world');
  }
}
 
Foo.bar() // hello

上面代码中,静态方法bar调用了this.baz,这里的this指的是Foo类,而不是Foo的实例,等同于调用Foo.baz。另外,从这个例子还可以看出,静态方法可以与非静态方法重名。

③、父类的静态方法,可以被子类继承。

class Foo {
  static classMethod() {
    return 'hello';
  }
}
 
class Bar extends Foo {
}
 
Bar.classMethod() // 'hello'

④、静态方法也是可以从super对象上调用的。

class Foo {
  static classMethod() {
    return 'hello';
  }
}
 
class Bar extends Foo {
  static classMethod() {
    return super.classMethod() + ', too';
  }
}
 
Bar.classMethod() // "hello, too"

五、类的私有属性和私有方法

私有方法和私有属性,是只能在类的内部访问的方法和属性,外部不能访问。这是常见需求,有利于代码的封装,但 ES6 不提供,只能通过变通方法模拟实现。

1、类的私有属性

目前,有一个提案,为 class 加了私有属性——在属性名之前,使用 # 表示。(了解)

class IncreasingCounter {
  #count = 0;
  get value() {
    console.log('Getting the current value!');
    return this.#count;
  }
  increment() {
    this.#count++;
  }
}

上面代码中,#count就是私有属性,只能在类的内部使用(this.#count)。如果在类的外部使用,就会报错。

const counter = new IncreasingCounter();
counter.#count // 报错
counter.#count = 42 // 报错

2、类的私有方法

①、创建类的私有方法,目前有两种方案:

  • 在命名上加以区别。
  • (提案)在私有方法前加上 # 。

--> 在命名上加以区别。

class Widget {
 
  // 公有方法
  foo (baz) {
    this._bar(baz);
  }
 
  // 私有方法
  _bar(baz) {
    return this.snaf = baz;
  }
 
  // ...
}

-->(提案)在私有方法前加上 # 。(了解)

class Foo {
  #a;
  #b;
  constructor(a, b) {
    this.#a = a;
    this.#b = b;
  }
  #sum() {
    return #a + #b;
  }
  printSum() {
    console.log(this.#sum());
  }
}

上面代码中,#sum()就是一个私有方法。

六、类的实例

1、创建类的实例

使用 new 命令生成类的实例。

class Point {
  // ...
}
 
var point = new Point(2, 3);

与 ES5 一样,实例的属性除非显式定义在实例本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)。

//定义类
class Point {
 
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
 
  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
 
}
 
var point = new Point(2, 3);
 
point.toString() // (2, 3)
 
point.hasOwnProperty('x') // true
point.hasOwnProperty('y') // true
point.hasOwnProperty('toString') // false
point.__proto__.hasOwnProperty('toString') // true

上面代码中,x和y都是实例对象point自身的属性(因为定义在this变量上),所以hasOwnProperty方法返回true,而toString是原型对象的属性(因为定义在Point类上),所以hasOwnProperty方法返回false。这些都与 ES5 的行为保持一致。

2、实例属性的新写法

实例属性除了定义在constructor()方法里面的this上面,也可以定义在类的最顶层。

class IncreasingCounter {
  constructor() {
    this._count = 0;
  }
  get value() {
    console.log('Getting the current value!');
    return this._count;
  }
  increment() {
    this._count++;
  }
}

上面代码中,实例属性this._count定义在constructor()方法里面。另一种写法是,这个属性也可以定义在类的最顶层,其他都不变。

class IncreasingCounter {
  _count = 0;
  get value() {
    console.log('Getting the current value!');
    return this._count;
  }
  increment() {
    this._count++;
  }
}

上面代码中,实例属性_count与取值函数value()和increment()方法,处于同一个层级。这时,不需要在实例属性前面加上this。

这种新写法的好处是,所有实例对象自身的属性都定义在类的头部,看上去比较整齐,一眼就能看出这个类有哪些实例属性。

3、通过实例为类添加方法

与 ES5 一样,类的所有实例共享一个原型对象。

var p1 = new Point(2,3);
var p2 = new Point(3,2);
 
p1.__proto__ === p2.__proto__
//true

这也意味着,可以通过实例的__proto__属性为“类”添加方法。

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"

上面代码在p1的原型上添加了一个printName方法,由于p1的原型就是p2的原型,因此p2也可以调用这个方法。而且,此后新建的实例p3也可以调用这个方法。这意味着,使用实例的__proto__属性改写原型,必须相当谨慎,不推荐使用,因为这会改变“类”的原始定义,影响到所有实例。

七、类的继承

1、extends 关键字

  • Class 可以通过 extends 关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
  • Class 相当于实例的原型,所有在 Class 中定义的普通的方法和属性,都会被实例继承。
class Point {
}
 
class ColorPoint extends Point {
}

2、 super 关键字

在任意一个对象中,都可以使用super关键字。

var obj = {
  toString() {
    return "MyObject: " + super.toString();
  }
};
 
obj.toString(); // MyObject: [object Object]

super 关键字,既可以当作函数使用,也可以当作对象使用。使用super的时候,必须显式指定是作为函数、还是作为对象使用,否则会报错。

class A {}
 
class B extends A {
  constructor() {
    super();
    console.log(super); // 报错
  }
}

(1)、super 关键字作为函数

ES6 规定,super 作为函数时:

  • 代表父类的构造函数,用来新建父类的 this 对象。
  • 必须且只能在子类 constructor 方法中调用一次 super() 函数。
class A {}
 
class B extends A {
  constructor() {
    super();
  }
}

在 JavaScript 中,super 指的是父类的构造函数。在上述代码中,super 指向 A.prototype.constructor.call(this)。

1⃣️、为什么必须在 constructor 方法中调用一次 super 方法呢?

因为子类自己的 this 对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。

如果子类不调用 super 方法,子类就得不到 this 对象,就会报错。

class Point { /* ... */ }
 
class ColorPoint extends Point {
  constructor() {
  }
}
 
let cp = new ColorPoint(); // ReferenceError

2⃣️、如果子类没有定义 constructor 方法,这个方法会被默认添加,不管有没有显式定义,任何一个子类都有 constructor 方法。

class ColorPoint extends Point {
}
 
// 等同于
class ColorPoint extends Point {
  constructor(...args) {
    super(...args);
  }
}

【注意】

不要将 react 在使用 ES6 的 class 语法时书写的 super(props) 看作一体,因为 props 不是 JavaScript 规定的。

(2)、super 关键字作为对象

super 作为对象时:

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

1⃣️、super 作为对象时,在普通方法中,指向父类的原型对象。

super 作为对象时,在普通方法中,指向父类的原型对象。所以,可以通过 super  调用父类的原型对象里的方法或属性,但是无法通过 super 调用定义在父类实例(this)上的方法或属性。

class A {
    constructor() {
        this.p = 2;
    }
    p() {
        return 2;
    }
}
 
class B extends A {
  constructor() {
    super();
    console.log(super.p()); // 2
    console.log(super.p); // undefined
  }
}
 
let b = new B();

上面代码中,子类B当中的super.p(),就是将super当作一个对象使用。这时,super在普通方法(log方法)中,指向A.prototype,所以super.p()就相当于A.prototype.p()。 此时,由于 super 指向父类的原型对象,所以定义在父类实例(this)上的方法或属性,是无法通过 super 调用的。

如果属性定义在父类的原型对象上,super 就可以取到了。

class A {}
A.prototype.x = 2;
 
class B extends A {
  constructor() {
    super();
    console.log(super.x) // 2
  }
}
 
let b = new B();

在子类普通方法中,通过 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();
  }
}
 
let b = new B();
b.m() // 2

2⃣️、super 作为对象时,在静态方法中,指向父类。

在子类的静态方法中通过 super 调用父类的方法时,方法内部的 this 指向当前的子类,而不是子类的实例。

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

(3)、使用 super 对象的注意事项

尽量不要在子类的 constructor 方法中使用 super 对象,因为它有时指向子类的原型对象,有时指向父类的原型对象。

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

上面代码中,super.x赋值为3,这时等同于对this.x赋值为3。而当读取super.x的时候,读的是A.prototype.x,所以返回undefined。 

3、ES6 允许原生构造函数被继承

ES5 的原生构造函数大致包括:

  • Boolean()
  • Number()
  • String()
  • Array()
  • Date()
  • Function()
  • RegExp()
  • Error()
  • Object()

在 ES5 中,它们不允许被继承,在 ES6 中,它们允许被继承。这是因为:

ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。
ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面,然后再用子类的构造函数修改this。使得父类的所有行为都可以继承。下面是一个继承Array的例子。
举个栗子:继承 Array

class MyArray extends Array {
  constructor(...args) {
    super(...args);
  }
}
 
var arr = new MyArray();
arr[0] = 12;
arr.length // 1
 
arr.length = 0;
arr[0] // undefined

可见,在 ES6 的中,extends 关键字不仅可以用来继承类,还可以用来继承原生的构造函数。

注意,继承Object的子类,有一个行为差异。

class NewObj extends Object{
  constructor(){
    super(...arguments);
  }
}
var o = new NewObj({attr: true});
o.attr === true  // false

上面代码中,NewObj 继承了 Object,但是无法通过 super 方法向父类 Object 传参。这是因为 ES6 改变了 Object 构造函数的行为:一旦发现 Object 方法不是通过 new Object() 这种形式调用的,Object 构造函数就会忽略这些参数。 

4、静态方法也可以被继承

class A {
  static hello() {
    console.log('hello world');
  }
}
 
class B extends A {
}
 
B.hello()  // hello world

上面代码中,hello()是A类的静态方法,B继承A,也继承了A的静态方法。 

5、判断一个类是否继承了另一个类

判断一个类是否继承了另一个类用 Object.getPrototypeOf() 方法。

Object.getPrototypeOf(ColorPoint) === Point
// true

6、类的 prototype 属性和__proto__属性 

在 ES5 中,每一个对象都有 __proto__ 属性,指向对应的构造函数的 prototype 属性。

Class 作为构造函数的语法糖,也有 prototype 属性和 __proto__ 属性,因此同时存在两条继承链。

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

class A {
}
 
class B extends A {
}
 
B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true

上面代码中,子类B的__proto__属性指向父类A,子类B的prototype属性的__proto__属性指向父类A的prototype属性。

这样的结果是因为,类的继承是按照下面的模式实现的:

class A {
}
 
class B {
}
 
// B 的实例继承 A 的实例
Object.setPrototypeOf(B.prototype, A.prototype);
 
// B 继承 A 的静态属性
Object.setPrototypeOf(B, A);
 
const b = new B();
 
Object.setPrototypeOf = function (obj, proto) {
  obj.__proto__ = proto;
  return obj;
}

子类实例的__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

也就是说,子类的原型的原型,是父类的原型。因此,通过子类实例的__proto__.__proto__属性,可以修改父类实例的行为。

p2.__proto__.__proto__.printName = function () {
  console.log('Ha');
};
 
p1.printName() // "Ha"

7、Mixin 模式的实现

Mixin 指的是多个对象合成一个新的对象,新对象具有各个组成成员的接口。

Mixin 的最简单实现如下:

const a = {
  a: 'a'
};
const b = {
  b: 'b'
};
const c = {...a, ...b}; // {a: 'a', b: 'b'}

上面代码中,c对象是a对象和b对象的合成,具有两者的接口。 

下面是一个更完备的实现,将多个类的接口“混入”(mix in)另一个类。

  class Mix {
    constructor() {
      for (let mixin of mixins) {
        copyProperties(this, new mixin()); // 拷贝实例属性
      }
    }
  }
 
  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);
    }
  }
}

上面代码的mix函数,可以将多个对象合成为一个类。使用的时候,只要继承这个类即可。

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

八、class 的使用

1、 ES6 与 ES7 的 class 的语法差异

以其在 react 中的使用为例,对比说明一下(下面的案例中使用了 react 的 PropTypes 进行类型检查,不了解的可先戳此链接了解一下):

ES6 的 class 中:

export class Counter extends React.Component {

  constructor(props) {
    super(props);
    this.state = {count: props.initialCount};
  }

  tick() {
    this.setState({count: this.state.count + 1});
  }

  render() {
    return (
      <div onClick={this.tick.bind(this)}>
        Clicks: {this.state.count}
      </div>
    );
  }
}

Counter.propTypes = { initialCount: React.PropTypes.number };
Counter.defaultProps = { initialCount: 0 };

ES7 的 class 中:

export class Counter extends React.Component {

  static propTypes = { initialCount: React.PropTypes.number };
  static defaultProps = { initialCount: 0 };

  state = { count: this.props.initialCount };

  tick() {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return (
      <div onClick={this.tick.bind(this)}>
        Clicks: {this.state.count}
      </div>
    );
  }
}

本文参考:ES6 入门教程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值