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(); // 1
var c2 = new C(); // 2
c1.count === 2; // true
c1.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委托到D
Object.setPrototypeOf(E,D);
E.foo();// "P foo"