关于类
- 类的数据类型就是函数,类本身就指向构造函数
class Point {
// ...
}
typeof Point // "function"
Point === Point.prototype.constructor // true
- 构造函数的
prototype
属性,在 ES6 的“类”上面继续存在。事实上,类的所有方法都定义在类的prototype
属性上面。
class Point {
constructor() {
// ...
}
toString() {
// ...
}
toValue() {
// ...
}
}
// 等同于
Point.prototype = {
constructor() {},
toString() {},
toValue() {},
};
- 因此,在类的实例上面调用方法,其实就是调用原型上的方法。
class B {}
const b = new B();
b.constructor === B.prototype.constructor
prototype
对象的constructor()
属性,直接指向“类”的本身,这与 ES5 的行为是一致的。
Point.prototype.constructor === Point // true
- 类的内部所有定义的方法,都是不可枚举的
Object.keys(Point.prototype)
// []
构造函数 constructor
constructor()
方法是类的默认方法,通过new
命令生成对象实例时,自动调用该方法。- 一个类必须有
constructor()
方法,如果没有显式定义,一个空的constructor()
方法会被默认添加。 constructor()
方法默认返回实例对象(即this
)
类跟普通构造函数的一个主要区别
- 类必须使用
new
调用,否则会报错 - 普通构造函数可以不用
new
也可以执行
在一个构造方法中可以使用super
关键字来调用一个父类的构造方法
class Square extends Polygon {
constructor(length) {
// 在这里, 它调用了父类的构造函数, 并将 lengths 提供给 Polygon 的"width"和"height"
super(length, length);
// 注意: 在派生类中, 必须先调用 super() 才能使用 "this"。
// 忽略这个,将会导致一个引用错误。
this.name = 'Square';
}
get area() {
return this.height * this.width;
}
set area(value) {
// 注意:不可使用 this.area = value
// 否则会导致循环call setter方法导致爆栈
this._area = value;
}
}
类的实例化
类的所有实例共享一个原型对象。
class B {
constructor(a, b)
this.a = a
this.b = b
}
const b = new B(2,1);
const c = new B(3,1);
b._proto_ == c._proto_ // true
可以通过实例的__proto__
属性为“类”添加方法
- 当通过一个实例给类的prototype添加方法的时候,别的实例也可以调用这个新添加的方法
- 使用实例的
__proto__
属性改写原型,必须相当谨慎,不推荐使用,因为这会改变“类”的原始定义,影响到所有实例。 - 而且
__proto__
不是语言本身的特性,谨慎使用
静态方法 static
类(class)通过 static 关键字定义静态方法。不能在类的实例上调用静态方法,而应该通过类本身调用。这些通常是实用程序方法,例如创建或克隆对象的功能。
- 注意,如果静态方法包含
this
关键字,这个this
指的是类,而不是实例 - 父类的静态方法,可以被子类继承。子类也可以调用
- 静态方法也是可以从
super
对象上调用的
class Foo {
static classMethod() {
return 'hello';
}
}
class Bar extends Foo {
static classMethod() {
return super.classMethod() + ', too';
}
}
Bar.classMethod() // "hello, too"
关键字 super
super关键字用于访问和调用一个对象的父对象上的函数。
在构造函数中使用时,super
关键字将单独出现,并且必须在使用this
关键字之前使用。super
关键字也可以用来调用父对象上的函数。
- 调用父类的构造函数
- 调用父类上的静态方法
- 子类 constructor 方法中必须有 super ,且必须出现在 this 之前。
- 调用父类构造函数,只能出现在子类的构造函数。
class Child extends Father { constructor() { super(); // super() 只能出现在子类的 constructor 中 } }
- 调用父类方法, super 作为对象,在普通方法中,指向父类的原型对象,在静态方法中,指向父类。
class Father { test() { return 0; } static test1() { return 1; } } class Child2 extends Father { constructor(){ super(); // 调用父类普通方法 console.log(super.test()); // 0 } static test3(){ // 调用父类静态方法 只有类才能调用 静态方法 return super.test1+2; } } Child2.test3(); // 3
继承 extends
子类通过extends
继承父类时,必须在constructor
方法中调用super
方法,否则子类通过new
关键字新建实例时会报错。
这是因为子类自己的this
对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,
然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super
方法,子类就得不到this
对象。
+ ES5 的继承,实质是先创造子类的实例对象`this`,然后再将父类的方法添加到`this`上面(`Parent.apply(this)`)。
+ ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到`this`上面(所以必须先调用`super`方法),然后再用子类的构造函数修改`this`。
+ 父类的静态方法,也会被子类继承。
class 不存在变量提升
new Foo(); // ReferenceError
class Foo {}
因为不存在变量提升,所以 使用 new
关键字的时候,Foo
还没有定义,所以抛错