1. class
直接上个例子先,一个UI控件按钮的实例
class Widget{constructor(width, height){this.width = width || 50;this.height = height || 50;this.$elem = null;}render($where){if(this.$elem){// 样式this.$elem.css({width: `${this.width}px`,height: `${this.height}px`// 放到哪个位置上}).appendTo($where)}}}class Button extends Widget {constructor(width,height,label){super(width,height);this.label = label || "Default";this.$elem = $("<button>").text(this.label);}render($where){super.render($where);this.$elem.click(this.onclick.bind(this)); // 绑定}onclick(evt){console.log(`Button ${this.label} clicked!`)}}
语法好看之外,还解决了什么❓问题
- 不再引用杂乱的.prototype了
- Button声明时,直接”继承“了Widget,不再需要通过Object.create(…)来替换.prototype对象
- 可以通过super(..)来实现相对多态,这样任何方法都可以引用原型链上层的同名方法,解决了之前构造函数的问题,构造函数不属于类,所以无法相互引用
- class字面语法不能声明属性,只能声明方法,这样子可以规避犯错
- 通过class .. extends .. 很自然地扩展对象(子)类型,甚至是内置的对象(子)类型,比如Aarray或者RegExp,使扩展变得容易了
2. class的陷阱
class只是语法糖🍬,本质上还是[[Prototype]](委托)机制的上的使用,并没有复制的功能
当你修改或者替换父类中的一个方法,那子类和所有实例都会受到影响,因为没有进行复制
class C{constructor() {this.num = Math.random();}rand(){console.log(`Random:${this.num}`);}}var c1 = new C();c1.rand();// Random:0.4324299// 修改了父类的方法C.prototype.rand = function (){console.log(`Random:${Math.round(this.num*1000)}`);}var c2 = new C();// 实例化对象都修改了c2.rand(); // "Random:867"c1.rand(); // "Random:432"
因为class语法无法定义类成员属性(只能定义方法),当你需要跟踪实例之间共享状态必须要使用.prototype语法
class C {constuctor() {// 确保修改的是共享状态而不是在实例上创建一个屏蔽属性C.prototype.count++// this.count可以通过委托实现我们想要的功能console.log(this.count);}}// 直接向prototype对象添加一个共享状态c.prototype.count = 0;var c1 = new C(); // 1var c2 = new C(); // 2c1.count === 2; // truec1.count === c2.count; // true
其实它也是违背class语法本意,在实现中暴露了.prototype,但是遇到这种情况也只能这样子用
还有个意外屏蔽的问题出现在class上面
class C {constructor() {// id属性会屏蔽了id()方法this.id = id;}id() {console.log("ID:" + id);}}var c1 = new C("c1");c1.id(); // Typeerror
还有一个,super的绑定方法,无论目前的方法在原型链中处于什么位置,super总会绑到链的上一层
class P{foo() {console.log("P foo")}}class C extends P{// super当作函数使用就是代表父类的构造函数// 相当于P.prototype.foo.call(this)foo(){super()}}var c1 = new C();c1.foo(); // "p foo"var D = {foo:function(){console.log("D foo")}}var E={foo:C.prototype.foo}// 把E委托到DObject.setPrototypeOf(E,D);E.foo();// "P foo"
