class是语法糖,它背后使用的仍然是原型和构造函数的概念。
定义
// 类声明class Person {}// 类表达式const Animal = class {};
与函数表达式类似,类表达式在它们被求值前也不能引用。
函数声明可以提升,但类定义不能。
函数受函数作用域限制,而类受块作用域限制
constructor
constructor 关键字用于在类定义块内部创建类的构造函数。
类实例化时传入的参数会用作构造函数的参数。不需要参数时,括号可省略。
class Person {constructor(name) {console.log(arguments.length);this.name = name || null;}}let p1 = new Person; // 0console.log(p1.name); // nulllet p2 = new Person(); // 0console.log(p2.name); // nulllet p3 = new Person('Jake'); // 1console.log(p3.name); // Jake
类构造函数与构造函数的主要区别是,调用类构造函数必须使用new 操作符。而普通构造函数如果不使用 new 调用,那么就会以全局的 this (通常是 window )作为内部对象。
类是一种特殊函数。
typeof Person // function
与立即调用函数表达式相似,类也可以立即实例化:
// 因为是一个类表达式,所以类名是可选的let p = new class Foo {constructor(x) {console.log(x);}}('bar'); // barconsole.log(p); // Foo {}
constructor内定义的属性和方法都在实例上
class Person {constructor() {this.name = new String('Jack');this.sayName = () =>console.log(this.name);this.nicknames = ['Jake', 'J-Dog']}}let p1 = new Person()
原型
在constructor外,class内,,可以为prototype定义method,但不能定义property
class Person {constructor() {// 添加到this的所有内容都会存在于不同的实例上this.locate = () =>console.log('instance');}// 在类块中定义的所有内容都会定义在类的原型上locate() {console.log('prototype');}}let p = new Person();p.locate(); // instancePerson.prototype.locate(); // prototype
在class之外可以定义实例和原型属性
class Person {sayName() {console.log('${Person.greeting} ${this.name}');}}// Define data member on classPerson.greeting = 'My name is';// Define data member on prototypePerson.prototype.name = 'Jake';let p = new Person();p.sayName(); // My name is Jake
getter/setter
class Person {set name(newName) {this.name_ = newName;}get name() {return this.name_;}}let p = new Person();p.name = 'Jake';console.log(p.name); // Jake
静态类方法
定义在class上的方法,this指向class, 关键字static
class Person {constructor() {// Everything added to 'this' will exist on each individual instancethis.locate = () => console.log('instance', this);}// Defined on the class prototype objectlocate() {console.log('prototype', this);}// Defined on the classstatic locate() {console.log('class', this);}}let p = new Person();p.locate(); // instance, Person {}Person.prototype.locate(); // prototype, {constructor: ... }Person.locate(); // class, class Person {}
Inheritance
Although it makes use of a new syntax, class inheritance still uses the prototype chain under the hood.
使用 extends 关键字,就可以继承任何拥有 [[Construct]] 和原型的对象(包括函数);
class Vehicle {}// Inherit from classclass Bus extends Vehicle {}let b = new Bus();console.log(b instanceof Bus); // trueconsole.log(b instanceof Vehicle); // truefunction Person() {}// Inherit from function constructorclass Engineer extends Person {}let e = new Engineer();console.log(e instanceof Engineer); // trueconsole.log(e instanceof Person); // true
super()
super用于派生类调用父类的构造函数,在派生的类中, 在你可以使用’this‘之前, 必须先调用super()。
super也可以用于静态方法。
如果父类的构造函数传了参数,super也要传相同的参数。
如果在派生类中显式定义了构造函数,则要么必须在其中调用 super() ,要么必须在其中返回一个对象。
class Vehicle {constructor() {this.hasEngine = true;}}class Bus extends Vehicle {constructor() {// Cannot reference 'this' before super(), will throw ReferenceErrorsuper(); // same as super.constructor()console.log(this instanceof Vehicle); // trueconsole.log(this); // Bus { hasEngine: true }}}new Bus();
抽象基类
抽象基类只能被继承,不能被实例化,通过new.target判断
/ 抽象基类class Vehicle {constructor() {if (new.target === Vehicle) {throw new Error('Vehicle cannot be directly instantiated');}if (!this.foo) {throw new Error('Inheriting class must define foo()');}console.log('success!');}}// 派生类class Bus extends Vehicle {foo() {}}// 派生类class Van extends Vehicle {}new Bus(); // success!new Van(); // Error: Inheriting class must define foo()
