class
class是一个语法糖,其底层还是通过 构造函数 去创建的。新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
es5实现
1 | function Point(x, y) { |
es6实现
1 | class Point { |
上面代码定义了一个“类”,可以看到里面有一个constructor方法,这就是构造方法,而this关键字则代表实例对象。也就是说,ES5 的构造函数Point,对应 ES6 的Point类的构造方法。
constructor 方法
constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。
1 | class A { |
constructor方法默认返回实例对象(即this),完全可以指定返回另外一个对象。
1 | class A { |
类的实例
- 生成类的实例的写法,与 ES5 完全一样,也是使用 new 命令。前面说过,如果忘记加上new,像函数那样调用Class,将会报错。
- 与 ES5 一样,实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class类上)
1 | class CppPerson { |
与 ES5 一样,类的所有实例共享一个原型对象。
静态方法
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
1 | class Foo { |
实例属性的新写法
实例属性除了定义在constructor()方法里面的this上面,也可以定义在类的最顶层。
1 | class IncreasingCounter { |
突然联想到目前的业务项目,做了2年才发现也是这样的写法,
1 | private item: number = 0 // 快捷导航选中index |
上面代码中,实例属性item与原型上的方法,处于同一个层级。这时,不需要在实例属性前面加上this。
这种新写法的好处是,所有实例对象自身的属性都定义在类的头部,看上去比较整齐,一眼就能看出这个类有哪些实例属性。
原本应该是这样写的
1 | default class VueGrid extends Vue { |
静态属性
静态属性指的是 Class 本身的属性,即Class.propName,而不是定义在实例对象(this)上的属性。
1 | class Foo { |
上面的写法为Foo类定义了一个静态属性prop。
目前,只有这种写法可行,因为 ES6 明确规定,Class 内部只有静态方法,没有静态属性。现在有一个提案提供了类的静态属性,写法是在实例属性的前面,加上static关键字。
1 | class MyClass { |
这个新写法大大方便了静态属性的表达。
1 | // 老写法 |
extends继承
Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
1 | class Point {} |
子类必须在constructor方法中调用super方法,否则新建实例时会报错。
这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。
super
在 super() 执行时,它指向的是 子类 B 的构造函数,而不是父类 A 的构造函数。也就是说,super() 内部的 this 指向的是 B
super()相当于Parent.prototype.constructor.call(this. props)
super本身是指向父类的构造函数但做函数调用后返回的是子类的实例,实际上做了parent.prototype.constructor.call(this),做对象调用时指向父类 parent.prototype,从而实现继承
super这个关键字,既可以当作函数 Function 使用,也可以当作对象 Object 使用。在这两种情况下,它的用法完全不同。
作函数 Function 使用
代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数。
1 |
|
虽然代表父类的构造函数,但是返回的是子类的实例,即super内部的this指的是B的实例,因此super()在这里相当于A.prototype.constructor.call(this)。
有点绕!!!
当作对象 Object 使用
super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
1 | class A { |
子类B当中的super.p(),就是将super当作一个对象使用。这时,super在普通方法之中,指向A.prototype,所以super.p()就相当于A.prototype.p()。注意是A类原型对象上的方法。
这里需要注意,由于super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的
1 | class Test { |
实例属性和方法: constructor里的,就是绑定的this,比如this.name
原型属性和方法: class里除了构造函数以内,其他的方法就是原型方法,原型属性需要用到Test.prototype来定义
静态属性和方法: static关键字标识,只能类本身调用,类的实例不能调用
私有属性和方法: ts里的private关键字
ES6 规定,在子类普通方法中通过super调用父类的方法时,方法内部的this指向当前的子类实例
1 | class A { |
如果super作为对象,用在静态方法之中,这时super将指向父类,而不是父类的原型对象。
1 | class Parent { |
上面代码中,super在静态方法之中指向父类,在普通方法之中指向父类的原型对象。
ES5 和 ES6继承区别
ES5 的继承,通过prototype或构造函数机制来实现, 实质是先创造子类的实例对象this,再将父类的方法添加到this上面(Parent.apply(this))。
ES6 的继承机制完全不同,实质是先创建父类的实例对象this(所以必须先调用父类的super()方法),然后再用子类的构造函数修改this。
另一个需要注意的地方是,在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,基于父类实例,只有super方法才能调用父类实例。
1 | class Point { |