我一直在构建一个小型 JS 框架以供我的工作使用,我想采用 Douglas Crockford 的原型继承模式。我想我对原型对象的工作原理有了大致的了解,但除了最简单的示例之外,我还不清楚我将如何使用这种模式。
我会充实到我理解的程度。
(function () {
'use strict';
var Vehicles = {};
Vehicles.Vehicle = function () {
this.go = function () {
//go forwards
};
this.stop = function () {
//stop
};
};
Vehicles.Airplane = Object.create(Vehicles.Vehicle());
}());
所以现在我的 Vehicles.Airplane 对象可以 go() 和 stop(),但我想要更多。我想为这个对象添加 takeOff() 和 land() 方法。之后我可以使用丑陋的点符号:
Vehicles.Airplane.takeOff = function () {
//take off stuff
}
但这似乎是错误的,特别是如果我要添加许多方法或属性。在这里提出的问题似乎与我的非常相似,但答案对我来说并不完全正确。答案表明我应该在使用 Object.create 之前构建一个对象字面量,并且我应该将该对象字面量传递给 create 方法。然而,在给出的示例代码中,看起来他们的新对象现在根本没有继承任何东西。
我希望的是一些类似于以下的语法:
Vehicles.Airplane = Object.create(Vehicles.Vehicle({
this.takeOff = function () {
//takeOff stuff
};
this.land = function () {
//land stuff
};
}));
我知道这个语法现在会被 Object.create 严重破坏,因为我当然是在传递 Vehicle.Vehicle 一个函数而不是一个对象文字。那是无关紧要的。我想知道我应该以什么方式将新属性构建到从另一个继承的对象中,而不必事后用点符号一次列出一个。
编辑:
Bergi,在对这个话题进行了一些痛苦的思考之后,我想我真的很想采用你所说的“古典模式”。这是我的第一次尝试(现在使用实际的代码片段而不是模拟假设 - 你甚至可以看到我蹩脚的方法存根):
CS.Button = function (o) {
o = o || {};
function init(self) {
self.domNode = dce('a');
self.text = o.text || '';
self.displayType = 'inline-block';
self.disabled = o.disabled || false;
self.domNode.appendChild(ctn(self.text));
if (o.handler) {
self.addListener('click', function () {
o.handler(self);
});
}
}
this.setText = function (newText) {
if (this.domNode.firstChild) {
this.domNode.removeChild(this.domNode.firstChild);
}
this.domNode.appendChild(ctn(newText));
};
init(this);
};
CS.Button.prototype = Object.create(CS.Displayable.prototype, {
constructor: {value: CS.Button, configurable: true}
});
CS.Displayable = function (o) { // o = CS Object
o = o || {};
var f = Object.create(new CS.Element(o));
function init(self) {
if (!self.domAnchor) {
self.domAnchor = self.domNode;
}
if (self.renderTo) {
self.renderTo.appendChild(self.domAnchor);
}
}
//Public Methods
this.addClass = function (newClass) {
if (typeof newClass === 'string') {
this.domNode.className += ' ' + newClass;
}
};
this.addListener = function (event, func, capture) {
if (this.domNode.addEventListener) {
this.domNode.addEventListener(event, func, capture);
} else if (this.domNode.attachEvent) {
this.domNode.attachEvent('on' + event, func);
}
};
this.blur = function () {
this.domNode.blur();
};
this.disable = function () {
this.disabled = true;
};
this.enable = function () {
this.disabled = false;
};
this.focus = function () {
this.domNode.focus();
};
this.getHeight = function () {
return this.domNode.offsetHeight;
};
this.getWidth = function () {
return this.domNode.offsetWidth;
};
this.hide = function () {
this.domNode.style.display = 'none';
};
this.isDisabled = function () {
return this.disabled;
};
this.removeClass = function (classToRemove) {
var classArray = this.domNode.className.split(' ');
classArray.splice(classArray.indexOf(classToRemove), 1);
this.domNode.className = classArray.join(' ');
};
this.removeListener = function () {
//Remove DOM element listener
};
this.show = function () {
this.domNode.style.display = this.displayType;
};
init(this);
};
CS.Displayable.prototype = Object.create(CS.Element.prototype, {
constructor: {value: CS.Displayable, configurable: true}
});
我应该很清楚并说它还没有完全起作用,但主要是我想听听你对我是否走在正确的轨道上的看法。您在示例的评论中提到了“特定于实例的属性和方法”。这是否意味着我的 this.setText 方法和其他方法放置错误,并且原型链上的后代项目无法使用?
此外,在使用时,似乎声明的顺序现在很重要(我无法访问 CS.Displayable.prototype,因为(我认为)首先列出了 CS.Button,而当时我没有定义 CS.Displayable我试图引用它)。这是我只需要处理和处理的事情(按照代码中的祖先顺序而不是我的强迫症字母顺序)还是我在那里也忽略了一些东西?