0 前言:
1. 不同于传统面向类的语言中父类和子类、子类和实例之间的关系(复制操作),在JavaScript中并没有复制,实现类这一设计模式依靠的是 [[Prototype]] ,故父类和子类、子类和实例之间只有委托关联。
1 正文:
1. 由于这一委托关联,在es6之前想要实现类,往往利用的是 [[Prototype]];
// 传统JavaScript的类应用(prototype)
// 父类
function Widget(width, height) {
this.width = width || 50;
this.height = height || 50;
this.$elem = null;
}
Widget.prototype.render = function ($where) {
if (this.$elem) { //
this.$elem.css({
width: this.width + "px",
height: this.height + "px"
}).appendTo($where);
}
};
// 子类
function Button(width, height, label) {
// 调用“super”构造函数
Widget.call(this, width, height);
this.label = label || "Default";
this.$elem = $("<button>").text(this.label);
}
// 让 Button“继承”Widget
Button.prototype = Object.create(Widget.prototype);
// 重写 render(..)
Button.prototype.render = function ($where) {
//“super”调用
Widget.prototype.render.call(this, $where);
this.$elem.click(this.onClick.bind(this));
};
Button.prototype.onClick = function (evt) {
console.log("Button '" + this.label + "' clicked!");
};
$(document).ready(function () {
var $body = $(document.body);
var btn1 = new Button(125, 30, "Hello");
var btn2 = new Button(150, 40, "World");
btn1.render($body);
btn2.render($body);
});
2. ES6 之后,新出现了 Class 这一类的概念,于此紧密相关的是 super 这一关键字;其最大的作用是:用于访问对象字面量或类的原型([[Prototype]])上的属性,或调用父类的构造函数。
根据原书给出的关于利用 Class 和 super 实现 JavaScript 中类的代码如下:
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($where);
this.$elem.click(this.onClick.bind(this));
}
onClick(evt) {
console.log("Button '" + this.label + "' clicked!");
}
}
$(document).ready(function () {
var $body = $(document.body);
var btn1 = new Button(125, 30, "Hello");
var btn2 = new Button(150, 40, "World");
btn1.render($body);
btn2.render($body);
});
3. 问题:
执行上述代码,浏览器会产生错误:
经排查发现,这是对super的使用上出现问题。
4. 解决
具体解决方法和思路如下
render($where) {
// super($where); //原书描述,经测试出错。应改为super.render($where);此处不应该是对父类中的变量进行引用,而是rander()这一方法本身。
// 因为根据 MDN 在对象字面量中使用super.prop:在子类中调用父类的方法,这是在Object.setPrototyprOf()的帮助下实现的;
// 故将 子类的原型设置为父类,这样super就能在父类上找到父类的方法。
super.render($where)
this.$elem.click(this.onClick.bind(this));
}