前言
写本《JavaScript简餐》系列文章的目的是记录在阅读学习《JavaScript高级程序设计(第4版)》一书时出现的各个知识点。虽是对读书的笔记和总结,但是希望它轻量、简洁、犀利,不会引起阅读疲劳,可以在碎片化时间和闲暇之余轻巧地沐浴一下知识点。每篇文章只针对一个小部分进行讲解式的梳理,来达到个人复习总结和分享知识的目的。
一、继承基础
ECMAScript6新增的特性中最出色的一个就是原生支持了类继承机制。省去了之前ES5中各种花里胡哨的继承方式。虽然类继承使用的是新语法,但背后依旧使用的是原型链。ES6支持单继承。使用extends关键字,就可以继承任何拥有[ [Construct] ]和原型的对象。很大程度上,这意味着不仅可以继承一个类,也可以继承普通的构造函数。来看一下具体例子:class Person {}
class Student extends Person {}
let stu = new Student();
console.log(stu instanceof Student); // true
console.log(stu instanceof Person); // true
function Animal() {}
class Dog extends Animal {}
let d = new Dog();
console.log(d instanceof Dog); // true
console.log(d instanceof Animal); // true
另外,类表达式中也可以使用extends关键字,如下所示:
let Student = class extends Person {}
二、super关键字
如果接触过JAVA一定会对super关键字感到非常亲切,在JavaScript中也同样有super关键字,这个关键字只能在派生类中使用,而且仅限于类构造函数、静态方法内部。在派生类构造函数中使用super可以调用父类构造函数。来看一下具体例子:class Person {
constructor() {
this.name = "Lucy";
}
}
class Student extends Person {
constructor() {
super();
}
}
let stu = new Student();
console.log(stu.name); // Lucy
在静态方法中可以通过super调用父类上定义的静态方法:
class Person {
static sayHi() {
console.log("Hi, there!");
}
}
class Student extends Person {
static greeting() {
super.sayHi();
}
}
Student.greeting(); // Hi, there!
在使用super关键字时要注意的几个问题:
- super只能在派生类构造函数和静态方法中使用。
- 不能单独引用super关键字,要么用它调用构造函数,要么用它引用静态方法。
- 调用super()会调用父类构造函数,并将返回的实例赋值给this。
class Person {}
class Student extends Person {
constructor() {
super();
console.log(this); // Student {}
}
}
let stu = new Student();
- 在派生类中调用super()时是可以给super传参的,此参数将传给父类的构造函数。
- 如果没有定义类构造函数,在实例化派生类时会调用super(),而且会传入所有传给派生类的参数。
class Person {
constructor(name) {
this.name = name;
}
}
class Student extends Person {}
console.log(new Student("Lucy")); // Student { name: 'Lucy' }
- 在派生类构造函数中,不能在调用super()之前引用this。
- 如果在派生类中显式定义了构造函数,则要么必须在其中调用super(),要么必须在其中返回一个对象。
三、抽象基类
有时候可能需要定义这样一个类,它可以供其他类继承,但本身不会被实例化。虽然ECMAScript没有专门支持这种语法,但通过new.target也很容易实现。new.target保存通过new关键字调用的类或函数。通过在实例化时检测new.target是不是抽象基类可以阻止对抽象基类的实例化。如下所示:class Person {
constructor() {
if (new.target === Person) {
throw new Error("Person cannot be directly instantiated!");
}
}
}
class Student extends Person {}
let stu = new Student();
console.log(stu); // Student {}
let p = new Person(); // Error: Person cannot be directly instantiated!