说明
ES6引入了 Class(类)这个概念,作为对象的模板,通过class关键字,可以定义类。
示例
//定义类
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
使用:
let a = new Point(1,2);
a.toString();
设定
1.类的属性名,可以采用表达式
let methodName = 'getArea';
class Square {
constructor(length) {
// ...
}
[methodName]() {
// ...
}
}
2.类和模块的内部,默认就是严格模式
3.constructor方法
constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。
constructor方法默认返回实例对象(即this),你也可以指定返回另外一个对象。
class Foo {
constructor() {
return Object.create(null);
}
}
4.类必须使用new调用,否则会报错
类跟普通构造函数不同,普通构造函数不用new也可以执行。
5.class表达式
与函数一样,类也可以使用表达式的形式定义。
const MyClass = class Me {
getClassName() {
return Me.name;
}
};
上面代码使用表达式定义了一个类。需要注意的是,这个类的名字是MyClass而不是Me,Me只在 Class 的内部代码可用,指代当前类。
如果类的内部没用到的话,可以省略Me,也就是可以写成下面的形式。
const MyClass = class { /* ... */ };
如果采用 Class 表达式,可以写出立即执行的 Class。
let person = new class {
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}('张三');
person.sayName(); // "张三"
6.类不存在变量提升
必须先定义类,才能使用类
7.类的方法内部如果含有this,它默认指向类的实例
8.“类名.name”可以获取class关键字后面的类名
9.在类的内部可以使用get和set关键字
使用get和set关键字,可以对某个属性设置存值函数和取值函数,拦截该属性的存取行为。
class MyClass {
constructor() {
// ...
}
get prop() {
return 'getter';
}
set prop(value) {
console.log('setter: '+value);
}
}
let inst = new MyClass();
inst.prop = 123;
// setter: 123
inst.prop
// 'getter'
10.类可以用static设置静态方法。父类的静态方法,可以被子类继承
11.ES6规定,Class 内部只有静态方法,没有静态属性(虽然通过某种方式可以实现同样的功能)
继承
Class 可以通过extends关键字实现继承。
class Point {
}
class ColorPoint extends Point {
}
通过extends关键字,可以继承父类的所有属性和方法。
extends的限制
- 子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。ES6 的继承机制实质是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this
- 如果子类没有定义constructor方法,这个方法会被默认添加
- 在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错
- 父类的静态方法,也会被子类继承
- Object.getPrototypeOf方法可以用来从子类上获取父类
关于super
super这个关键字,既可以当作函数使用,也可以当作对象使用。
1.当super作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数。
class A {}
class B extends A {
constructor() {
super();
}
}
注意,super虽然代表了父类A的构造函数,但是返回的是子类B的实例,即super内部的this指的是B。
class A {
constructor() {
console.log(new.target.name);
}
}
class B extends A {
constructor() {
super();
}
}
new A() // A
new B() // B
上面代码中,new.target指向当前正在执行的函数。可以看到,在super()执行时,它指向的是子类B的构造函数,而不是父类A的构造函数。也就是说,super()内部的this指向的是B。
作为函数时,super()只能用在子类的构造函数之中,用在其他地方就会报错。
2.当super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
在普通方法中,由于super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的。
class A {
constructor() {
this.p = 2;
}
}
class B extends A {
get m() {
return super.p;
}
}
let b = new B();
b.m // undefined
但是如果属性定义在父类的原型对象上,super就可以取到。
class A {}
A.prototype.x = 2;
class B extends A {
constructor() {
super();
console.log(super.x) // 2
}
}
let b = new B();
另外,ES6 规定,在子类普通方法中通过super调用父类的方法时,方法内部的this指向当前的子类实例。
class A {
constructor() {
this.x = 1;
}
print() {
console.log(this.x);
}
}
class B extends A {
constructor() {
super();
this.x = 2;
}
m() {
super.print();
}
}
let b = new B();
b.m() // 2