ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法

类和构造函数的区别是什么?

1.类必须要new来调用,否则就会报错,普通的构造函数不用new来调用 也可以执行
2.类上的内部定义的方法都是不可枚举的,构造函数通过protype定义的方法是可以枚举的。
3.类不存在变量的提升,因为 ES6 不会把类的声明提升到代码头部。这种规定的原因与下文要提到的继承有关,必须保证子类在父类之后定义

类

ES6 的类,完全可以看作构造函数的另一种写法。
类的数据类型就是函数,类的本身就指向构造函数(只有构造函数才有prototype属性)
使用的时候使用new的命令来创建实例,跟构造函数的用法完全一致

  1. class Point {
  2. // ...
  3. }
  4. typeof Point // "function"
  5. Point === Point.prototype.constructor // true

构造函数的prototype属性,在ES6 的’类’上面继续存在。事实上,类的所有的方法都定义在类的prototype属性上面。
下面这些方法其实都是定义在Point.prototype 上面。

  1. class Point {
  2. constructor() {
  3. // ...
  4. }
  5. toString() {
  6. // ...
  7. }
  8. toValue() {
  9. // ...
  10. }
  11. }
  12. // 等同于
  13. Point.prototype = {
  14. constructor() {},
  15. toString() {},
  16. toValue() {},
  17. };

在类的实例上面调用方法,事实就是调用原型上的方法:
代码中,b是B类的实例,它的constructor()方法就是B类原型的constructor()方法

  1. class B {}
  2. const b = new B();
  3. b.constructor === B.prototype.constructor // true

另外,类的内部所有定义的方法,都是不可枚举的(non-enumerable)

  1. class Point {
  2. constructor(x, y) {
  3. // ...
  4. }
  5. toString() {
  6. // ...
  7. }
  8. }
  9. Object.keys(Point.prototype)
  10. // []
  11. Object.getOwnPropertyNames(Point.prototype)
  12. // ["constructor","toString"]

代码采用ES5的写法,toString()方法就是可枚举的

  1. var Point = function (x, y) {
  2. // ...
  3. };
  4. Point.prototype.toString = function () {
  5. // ...
  6. };
  7. Object.keys(Point.prototype)
  8. // ["toString"]
  9. Object.getOwnPropertyNames(Point.prototype)
  10. // ["constructor","toString"]

constructor 方法

constructor()方法是类的默认方法,通过new 命令生成对象实例时,自动调用改方法,一个类必须有constructor()方法,如果没有显式定义,一个空的constructor()方法会默认添加。

constructor() 方法默认返回实例对象(即this),但完全可以指定返回另外一个对象。
代码中,constructor()函数返回一个全新的对象,结果导致实例对象不是Foo类的实例

  1. class Foo {
  2. constructor() {
  3. return Object.create(null);
  4. }
  5. }
  6. new Foo() instanceof Foo
  7. // false

类的实例

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

  1. //定义类
  2. class Point {
  3. constructor(x, y) {
  4. this.x = x;
  5. this.y = y;
  6. }
  7. toString() {
  8. return '(' + this.x + ', ' + this.y + ')';
  9. }
  10. }
  11. var point = new Point(2, 3);
  12. point.toString() // (2, 3)
  13. point.hasOwnProperty('x') // true
  14. point.hasOwnProperty('y') // true
  15. point.hasOwnProperty('toString') // false
  16. point.__proto__.hasOwnProperty('toString') // true

静态方法

类相当与实例的原型,所有在类中的定义的方法,都会被实例继承,如果在一个方法前,加上static 关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为静态的方法。
注意,如果静态方法包含this关键字,这个this指的是类,而不是实例。

  1. class Foo {
  2. static classMethod() {
  3. return 'hello';
  4. }
  5. }
  6. Foo.classMethod() // 'hello'
  7. var foo = new Foo();
  8. foo.classMethod()
  9. // TypeError: foo.classMethod is not a function

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

  1. class Foo {
  2. static bar() {
  3. this.baz();
  4. }
  5. static baz() {
  6. console.log('hello');
  7. }
  8. baz() {
  9. console.log('world');
  10. }
  11. }
  12. Foo.bar() // hello

父类的静态方法,可以被子类继承(注意一下:这里就是用子类的调用方法,不是new出来的实例调用方法)

  1. class Foo {
  2. static classMethod() {
  3. return 'hello';
  4. }
  5. }
  6. class Bar extends Foo {
  7. }
  8. Bar.classMethod() // 'hello'

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

  1. class Foo {
  2. static classMethod() {
  3. return 'hello';
  4. }
  5. }
  6. class Bar extends Foo {
  7. static classMethod() {
  8. return super.classMethod() + ', too';
  9. }
  10. }
  11. Bar.classMethod() // "hello, too"

静态属性

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

  1. class Foo {
  2. }
  3. Foo.prop = 1;
  4. Foo.prop // 1

因为 ES6 明确规定,Class 内部只有静态方法,没有静态属性。现在有一个提案提供了类的静态属性,写法是在实例属性的前面,加上static关键

  1. // 老写法
  2. class Foo {
  3. // ...
  4. }
  5. Foo.prop = 1;
  6. // 新写法
  7. class Foo {
  8. static prop = 1;
  9. }