一般来说,应该在构造函数中而不是在构造函数中定义对象的每个实例的属性,例如total
和distance
旨在被初始化为完全相同的值。subSection
prototype
对象prototype
基本上是一个“模板”,该对象的每个实例都基于该模板。每个实例都有在prototype
.
因此这三个“类”(JavaScript 没有像 Java 这样的经典继承语言中定义的类,但有时习惯这些语言的人更容易使用该术语)应该声明如下:
function subSection(pattern) {
this.pattern = pattern;
}
function enhancer(pattern) {
this.pattern = pattern;
}
function silencer(pattern) {
this.pattern = pattern;
}
移出构造函数并total
进入distance
原型:subSection
subSection
subSection.prototype.total = 0;
subSection.prototype.distance = 0;
下一步是在 之间设置原型继承subSection
,enhancer
并silencer
使用无操作函数作为它们之间的一种代理:
function fn() {}
fn.prototype = subSection.prototype;
enhancer.prototype = new fn();
silencer.prototype = new fn();
最后,将和原型的constructor
属性设置为正确的对象,以便实例的属性引用正确的构造函数。如果我们不采取这一步,那么or的任何实例都会错误地引用构造函数。enhancer
silencer
constructor
enhancer
silencer
subSection
enhancer.prototype.constructor = enhancer;
silencer.prototype.constructor = silencer;
现在我们可以实例化enhancer
andsilencer
对象,生成的实例将具有total
anddistance
属性以及constructor
引用相应对象的属性。
var a, b;
a = new enhancer('aaaa');
b = new silencer('bbbb');
console.log(a.total + ', ' + a.distance); // 0, 0
console.log(b.total + ', ' + b.distance); // 0, 0
console.log(a.constructor); // (string representation of enhancer constructor)
我们fn
在继承过程中使用函数作为代理的原因是因为一个对象应该能够从另一个对象继承,而该对象不一定具有相同的构造函数参数集。
假设我们有以下两个构造函数:
function Person(name, age, hometown) {
this.name = name;
this.age = age;
this.hometown = hometown;
}
function Employee(name, company, title) {
this.name = name;
this.company = company;
this.title = title;
}
Employee
Person
即使它们不共享同一组构造函数参数,也应该能够继承自。如果我们简单地将Employee.prototype
的新实例设置为相等,Person
则Employee
原型将具有三个未定义的属性name
和。age
hometown
Employee.prototype = new Person; // name, age and hometown are undefined
var e = new Employee('John Smith', 'New York Times', 'editor'); // e's prototype has undefined properties name, age and hometown
通过使用 no-op 函数fn
(我们几乎可以将它命名为任何我们想要的名称),我们最终为每个实例都提供了一个干净的原型,因为fn
作为构造函数不需要任何参数。
这不是必需的,但它是设置原型继承的一种更简洁的方法:没有太多理由将带有未定义属性的原型弄乱。