类
class 关键字是 ES6 新增的。类(class)是ECMAScript 中新的基础性语法糖,本质上还是一个函数,但实际上它使用的仍然是原型和构造函数的概念。并且类受块级作用域限制。
class Person { }
console.log(Person);// class Person { }
定义类
类定义中的代码默认都在严格模式下执行。类包含如下可选内容:
- 构造函数方法
- 实例方法
- 获取函数
- 设置函数
- 静态类方法。
空的类定义照样有效。
- 类声明
类定义没有像函数那样的声明提升。
class Person {}
- 类表达式
类表达式不能在它们被求值前使用。
const Animal = class {};
类的组成
构造函数
在使用 new 操作符创建类的新实例时,会调用 constructor 方法,主要作用是初始化数据。不是必需的,不定义构造函数相当于将构造函数定义为空函数。
使用 new 调用类的构造函数会执行如下操作。
- 在内存中创建一个新对象。
- 这个新对象内部的 [[Prototype]] 指针被赋值为构造函数的 prototype 属性。
- 构造函数内部的 this 被赋值为这个新对象(即 this 指向新对象)。
- 执行构造函数内部的代码(给新对象添加属性)。
- 如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象。
class Animal { }
class Person {
constructor(name) {
this.name = name;
}
}
let animal = new Animal();
let person = new Person('孤城浪人');
console.log(animal); //Animal {}
console.log(person); //Person {name: '孤城浪人'}
默认情况下,constructor 会在执行之后返回当前实例对象。如果 constructor 显示的 return 了一个对象,那么这个对象不会通过 instanceof 操作符检测出跟类有关联,因为这个对象的原型指针并没有被修改。
class Person {
constructor(name) {
this.foo = name;
if (name) {
return {
name: 'XXXXX'
};
}
}
}
let p1 = new Person(),
p2 = new Person('孤城浪人');
console.log(p1); // Person {foo: undefined}
console.log(p1 instanceof Person); // true
console.log(p2); // {name: 'XXXXX'}
console.log(p2 instanceof Person); // false
我们可以调用实例的 constructor 方法,因为他就是普通函数,只是必须使用 new 关键字调用罢了。
class Person {
constructor(name) {
this.foo = name;
}
}
let p1 = new Person('孤城浪人'),
p2 = new p1.constructor('XXXXX');
console.log(p1); // Person {foo: undefined}
console.log(p2); // Person {name: 'XXXXX'}
类可以立即调用
const person = new class Person {
constructor(name) {
this.name = name;
}
}('孤城浪人')
console.log(person);// Person {name: '孤城浪人'}
成员
每次通过 new 调用类标识符时,都会执行类构造函数,这也就意味着在 constructor 中定义的成员每个实例都是一份新的,不会共享。但是不是在 constructor 中定义的成员(不能是原始值或对象)是定义在类的原型上,所有类的实例共享。
const person = class Person {
constructor(name) {
// 不共享
this.name = name;
}
// 定义在类原型上共享
sayName() {
console.log(this.name);
}
}
类定义也支持获取和设置访问器,方法名支持字符串、符号或计算的值。
class Person {
set name(newName) {
this.name_ = newName;
}
get name() {
return this.name_;
}
['sa' + 'y']() {
console.log('xxxx');
}
}
new Person().say();
静态方法
类可以定义静态方法。它的调用不依赖于实例,静态方法的方法名不能重复。
class Person {
static sayHellow() {
console.log('Hellow');
}
static say() {
console.log('xxxx');
}
}
Person.say();//Hellow
Person.sayHellow();//xxxx
迭代
类支持在原型和类本身定义生成器方法,因此可以添加一个默认的迭代器,把类实例变成可迭代对象。
class Person {
constructor() {
this.names = ['孤城浪人', 'xxxx']
}
// [Symbol.iterator]() {
// return this.names.entries();
// }
*[Symbol.iterator]() {
yield* this.names.entries();
}
}
const person = new Person();
for (let item of person) {
console.log(item);
}
// [0, '孤城浪人']
// [1, 'xxxx']
继承
这是类最优秀的特性,原生支持继承,但是本质上还是利用了原型链。使用 extends 关键字,就可以继承任何拥有[[Construct]]和原型的对象,前面说过类就是一个语法糖,他本质上是一个函数,所以 extends 不仅可以继承一个类,也可以继承普通的构造函数。并且 this 永远指向当前实例。
function Person() { };
class Father extends Person {
say() {
console.log(this);
}
}
class Son extends Father { }
const son = new Son();
console.log(son instanceof Father);//true
console.log(son instanceof Person);//true
son.say();//Son{}
这个例子可以看到 Father 继承 Person,Son 继承 Father,所以最后 Son 的实例的原型链中有 Father
和 Person 的原对象,因此 instanceof 返回 true。
super
如果子类需要调用父类的构造函数 constructor 初始化数据,那么只能在子类的构造函数中使用 super 关键字,此外在实例方法和静态方法内部也可以使用 super 关键字。(ES6 给类构造函数和静态方法添加了内部特性 [[HomeObject]] 指向定义该方法的对象。 super 始终会定义为 [[HomeObject]] 的原型)
注意:子类若定义了 constructor 则一定要调用 super,并且不要在调用 super 之前引用 this,否则会抛出 ReferenceError,
class Father {
constructor(name) {
this.name = name;
}
say() {
console.log(this);
}
}
class Son extends Father {
constructor() {
super('孤城浪人');
}
sayName() {
console.log('sayName')
super.say();
}
}
const son = new Son();
son.sayName();
// sayName
// Son { name: '孤城浪人' }
如果子类没有构造函数,在实例化子类时会自动调用 super(),而且会把所有传给子类的参数作为 super 的参数传入。
class Father {
constructor(name) {
this.name = name;
}
say() {
console.log(this);
}
}
class Son extends Father {}
const son = new Son('孤城浪人');
son.say();
//Son { name: '孤城浪人' }
抽象基类
抽象基类就是可供其他类继承,但本身不会被实例化的类。可以在构造函数中使用 new.target 属性阻止抽象基类实例化,也可以在其中强制子类要定义某个方法。
class Father {
constructor(name) {
if (new.target == Father) {
throw new Error('不能实例化');
}
if (!this.say) {
throw new Error('缺少say方法');
}
}
}
class Son extends Father {
constructor(name) {
super();
this.name = name;
}
say() {
console.log(this.name);
}
}
const son = new Son('孤城浪人');
son.say();//孤城浪人
new Father()
我是孤城浪人,一名正在前端路上摸爬滚打的菜鸟,欢迎你的关注。