ES6 类class

一、简介

JavaScript 语言中,生成实例对象的传统方法是通过构造函数。下面是一个例子。

function Person(name, age){
  this.name = name;
  this.age = age;
}

Person.prototype.showName = function(){
  return this.name;
}

var p1 = new Person('jack', 24);
console.log(p1.showName());  // jack

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。

基本上,ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用 ES6 的 class 改写,就是下面这样。

//定义类
class Person{
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  showName() {
    return this.name;
  }
}

上面代码定义了一个“类”,可以看到里面有一个 constructor 方法,这就是构造方法,而 this 关键字则代表实例对象。也就是说,ES5 的构造函数 Person,对应 ES6 的 Person 类的构造方法。

Person 类除了构造方法,还定义了一个 showName 方法。注意,定义“类”的方法的时候,前面不需要加上 function 这个关键字,直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错。

ES6 的类,完全可以看作构造函数的另一种写法。

class Person{
}

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

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

使用的时候,也是直接对类使用 new 命令,跟构造函数的用法完全一致。

二、严格模式

严格模式
类和模块的内部,默认就是严格模式,所以不需要使用use strict指定运行模式。只要你的代码写在类或模块之中,就只有严格模式可用。

考虑到未来所有的代码,其实都是运行在模块之中,所以 ES6 实际上把整个语言升级到了严格模式。

三、constructor 方法

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

class Person {
}

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

四、Class 的继承

Class 可以通过 extends 关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。

class Person {
}

class Man extends Person {
}

上面代码定义了一个 Man 类,该类通过 extends 关键字,继承了 Person 类的所有属性和方法。但是由于没有部署任何代码,所以这两个类完全一样,等于复制了一个 Person 类。下面,我们在 Man 内部加上代码。

class Man extends Person {
  constructor(name, age, job) {
    super(name, age);                                              //  调用父类的constructor(name, age)
    this.job = job;
  }

  showJob() {
    return 'My name is ' + super.showName() + ',I am a ' + this.job;   // 调用父类的 sayName()
  }
}

var person1 = new Man('jack',26,'FED');
console.log(person1.sayJob()) // "My name is jack,I am a FED"

上面代码中,constructor 方法和 showJob 方法之中,都出现了super关键字,它在这里表示父类的构造函数,用来新建父类的 this 对象。

子类必须在 constructor 方法中调用 super 方法,否则新建实例时会报错。这是因为子类没有自己的 this
对象,而是继承父类的 this 对象,然后对其进行加工。如果不调用 super 方法,子类就得不到 this 对象。

class Person { 
}

class Man extends Person {
  constructor() {
  }
}

var man = new Man(); // ReferenceError

上面代码中,Man 继承了父类 Person,但是它的构造函数没有调用 super 方法,导致新建实例时报错。

ES5 的继承,实质是先创造子类的实例对象 this,然后再将父类的方法添加到 this 上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改 this

如果子类没有定义 constructor 方法,这个方法会被默认添加,代码如下。也就是说,不管有没有显式定义,任何一个子类都有 constructor 方法。

class Man extends Person {
}

// 等同于
class Man extends Person {
  constructor(...args) {
    super(...args);
  }
}

另一个需要注意的地方是,在子类的构造函数中,只有调用 super 之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,是基于对父类实例加工,只有 super 方法才能返回父类实例。

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

class Man extends Person {
  constructor(name, age, job) {
    this.job = job; // ReferenceError
    super(name, age);
    this.job = job; // 正确
  }
}

上面代码中,子类的 constructor 方法没有调用 super 之前,就使用 this 关键字,结果报错,而放在
super 方法之后就是正确的。
下面是生成子类实例的代码。

let man = new Man('jack', 26, 'FED');

man instanceof Man // true
man instanceof Person // true

上面代码中,实例对象 man 同时是 ManPerson 两个类的实例,这与 ES5 的行为完全一致。

最后,父类的静态方法,也会被子类继承。

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

class B extends A {
}

B.hello()  // hello world

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

五、super关键字

super 这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同。

第一种情况,super 作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super 函数。

class A {}

class B extends A {
  constructor() {
    super();
  }
}

上面代码中,子类B的构造函数之中的super(),代表调用父类的构造函数。这是必须的,否则 JavaScript 引擎会报错。
(略)

参考:

  • Class的基本语法
  • Class 的继承
  • 视频 ECMAScript 6快速入门

你可能感兴趣的:(ES6 类class)