本文内容
类的定义
class Person {
// Person 的构造函数
constructor(name) {
this.name = name;
}
// 等价于 Person.prototype.sayName
sayName() {
console.log(this.name);
}
}
let person = new Person('czp');
person.sayName(); // czp
console.log(person instanceof Person); // true
console.log(person instanceof Object); // true
console.log(typeof Person); // function
console.log(typeof Person.prototype.sayName); // function
类只是个语法糖
需要注意以下内容
- 类声明类似于 let 声明,不能被提升,真正执行声明语句之前,一直存在于临时死区
- 类中所有代码运行在严格模式下,无法变更
- 类声明中,所有方法都是不可枚举的
- 只能使用 new 来调用类的构造函数,上例中如果直接调用
Person()将会报错 - 在类中修改类名会报错(在声明语句完成后是可以的)
访问器属性
上面的定义中,我们在构造函数中定义类的数据属性,我们也可以定义类的访问器属性
class CustomHTMLElement {
constructor(element) {
this.element = element;
}
get html() {
return this.element.innerHTML;
}
set html(value) {
this.element.innerHTML = value;
}
}
这个属性也是不可枚举的
可计算成员名称
let methodName = 'sayName';
class Person {
constructor(name) {
this.name = name;
}
[methodName]() {
console.log(this.name);
}
}
let me = new Person('czp');
me.sayName(); // czp
可以看出来,几乎跟对象字面量一样的用法
静态成员
在 ES 5 之前,模拟一个类的静态成员很简单,就是在构造函数上添加该属性
function Person(name) {
this.name = name;
}
Person.staticAttribute = 1;
使用 ES 6 的类语法则是如下,使用 static 关键字
let methodName = 'sayName';
class Person {
constructor(name) {
this.name = name;
}
[methodName]() {
console.log(this.name);
}
// 等价于 Person.hhh = 这个方法
static hhh() {
console.log('hhhhhhh');
}
}
Person.hhh(); // hhhhhhh
其实这种挺蠢的,实例对象没法访问所谓的静态成员
方法和访问器属性可以添加 static 关键字
继承
class Rectangle {
constructor(length, width) {
this.length = length;
this.width = width;
}
getArea() {
return this.length * this.width;
}
}
class Square extends Rectangle {
constructor(length) {
// 等价于 Rectangle.call(this,length,length)
super(length, length);
}
}
let square = new Square(3);
console.log(square.getArea()); // 9
console.log(square instanceof Square); // true
console.log(square instanceof Rectangle); // true
派生类中必须调用超类的构造方法
如果我们不写子类的构造方法,那么实际上子类会具有一个传入所有参数给超类的构造方法,例如
class Square extends Rectangle {
// 没有构造方法
}
等价于
class Square extends Rectangle {
constructor(...args) {
super(...args);
}
}
使用 super() 时有以下注意点
- 只能在子类的构造方法中调用
super() - 在构造方法中访问
this之前,一定要先调用super(),否则会报错 - 如果不想调用
super(),唯一的方法是让构造方法返回一个对象
类方法遮蔽
子类会覆盖超类的同名方法,这个很好理解
class Rectangle {
constructor(length, width) {
this.length = length;
this.width = width;
}
getArea() {
return this.length * this.width;
}
}
class Square extends Rectangle {
constructor(length) {
// 等价于 Rectangle.call(this,length,length)
super(length, length);
}
getArea() {
return super.getArea();
}
}
这里子类就是去调用超类的同名方法了,我们也可以完全重写属于子类自己的逻辑
静态成员继承
class Rectangle {
constructor(length, width) {
this.length = length;
this.width = width;
}
getArea() {
return this.length * this.width;
}
static create(length, width) {
return new Rectangle(length, width);
}
}
class Square extends Rectangle {
constructor(length) {
// 等价于 Rectangle.call(this,length,length)
super(length, length);
}
getArea() {
return super.getArea();
}
}
let rect = Square.create(3, 4);
console.log(rect instanceof Rectangle); // true
console.log(rect.getArea()); // 12
console.log(rect instanceof Square); // false
子类这个函数本身也继承了静态成员
但是行为都是 Rectangle.create 的行为
new.target
class Rectangle {
constructor(length, width) {
this.length = length;
this.width = width;
console.log(new.target);
}
getArea() {
return this.length * this.width;
}
static create(length, width) {
return new Rectangle(length, width);
}
}
class Square extends Rectangle {
constructor(length) {
// 等价于 Rectangle.call(this,length,length)
super(length, length);
}
getArea() {
return super.getArea();
}
}
new Rectangle(1, 2); // [Function: Rectangle]
new Square(1); // [Function: Square]
- 当我们使用 new 调用超类的构造方法,
new.target就是超类的构造方法 - 当我们使用 new 调用子类的构造方法,
new.target就是子类的构造方法 - 当不适用 new 调用,
new.target就是undefined