class是语法糖,它背后使用的仍然是原型和构造函数的概念。
定义
// 类声明
class Person {}
// 类表达式
const Animal = class {};
与函数表达式类似,类表达式在它们被求值前也不能引用。
函数声明可以提升,但类定义不能。
函数受函数作用域限制,而类受块作用域限制
constructor
constructor 关键字用于在类定义块内部创建类的构造函数。
类实例化时传入的参数会用作构造函数的参数。不需要参数时,括号可省略。
class Person {
constructor(name) {
console.log(arguments.length);
this.name = name || null;
}
}
let p1 = new Person; // 0
console.log(p1.name); // null
let p2 = new Person(); // 0
console.log(p2.name); // null
let p3 = new Person('Jake'); // 1
console.log(p3.name); // Jake
类构造函数与构造函数的主要区别是,调用类构造函数必须使用new 操作符。而普通构造函数如果不使用 new 调用,那么就会以全局的 this (通常是 window )作为内部对象。
类是一种特殊函数。
typeof Person // function
与立即调用函数表达式相似,类也可以立即实例化:
// 因为是一个类表达式,所以类名是可选的
let p = new class Foo {
constructor(x) {
console.log(x);
}
}('bar'); // bar
console.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(); // instance
Person.prototype.locate(); // prototype
在class之外可以定义实例和原型属性
class Person {
sayName() {
console.log('${Person.greeting} ${this.name}');
}
}
// Define data member on class
Person.greeting = 'My name is';
// Define data member on prototype
Person.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 instance
this.locate = () => console.log('instance', this);
}
// Defined on the class prototype object
locate() {
console.log('prototype', this);
}
// Defined on the class
static 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 class
class Bus extends Vehicle {}
let b = new Bus();
console.log(b instanceof Bus); // true
console.log(b instanceof Vehicle); // true
function Person() {}
// Inherit from function constructor
class Engineer extends Person {}
let e = new Engineer();
console.log(e instanceof Engineer); // true
console.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 ReferenceError
super(); // same as super.constructor()
console.log(this instanceof Vehicle); // true
console.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()