问题描述
我一直在构建一个小型JS框架供我工作使用,我想使用Douglas Crockford的原型继承模式。我想我对原型对象的工作方式有了一般的了解,但不清楚的是,除了最简单的例子之外,我将使用这种模式的方式。
I've been building a small JS framework for use at my job, and I'd like to employ Douglas Crockford's prototypical inheritance patterns. I think I get the general idea of how the prototype object works, but what isn't clear is the way in which I would use this pattern beyond the simplest example.
我会把它充实到理解的程度。
I'll flesh it out to the point that I understand it.
(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对象可以去()和停止(),但我想要更多。我想将takeOff()和land()方法添加到此对象。之后我可以使用丑陋的点符号:
So now my Vehicles.Airplane object can go() and stop(), but I want more. I want to add takeOff() and land() methods to this object. I could just use ugly dot notation afterwards:
Vehicles.Airplane.takeOff = function () {
//take off stuff
}
但这似乎不对,特别是如果我要添加许多方法或属性。 似乎与我的非常相似,但答案并不完全对我来说是真的。答案表明我应该在使用Object.create之前构建一个对象文字,并且我应该将该对象文字传递给create方法。但是,在给出的示例代码中,看起来它们的新对象现在根本没有继承。
But that seems wrong, especially if I were to add many methods or properties. The question asked at here seems to be very similar to mine, but the answer doesn't quite ring true for me. The answer suggests that I should build an object literal before using Object.create, and that I should pass that object literal into the create method. In the example code given, however, it looks like their new object inherits nothing at all now.
我希望的是一些语法类似于:
What I'm hoping for is some syntax similar to:
Vehicles.Airplane = Object.create(Vehicles.Vehicle({
this.takeOff = function () {
//takeOff stuff
};
this.land = function () {
//land stuff
};
}));
我知道这个语法现在会用Object.create打破,因为我当然正在通过Vehicle.Vehicle一个函数而不是一个对象文字。那就是重点。我想知道我应该以什么方式将一个新属性构建到一个继承自另一个对象的对象中,而不必在事后用点符号一次列出一个。
I know this syntax will break terribly with Object.create right now, because of course I'm passing Vehicle.Vehicle a function rather than an object literal. That's beside the point. I'm wondering in what way I should build new properties into an object that inherits from another without having to list them out one at a time with dot notation after the fact.
编辑:
Bergi,在对这个主题有些痛苦的想法之后,我想我真的想跟你所描述的一样作为古典模式。这是我的第一次尝试(现在使用实际的代码片段,而不是模拟假设 - 你甚至可以看到我糟糕的方法存根):
Bergi, after some anguished thought on the topic, I think I really want to go with what you described as the "Classical Pattern". Here is my first stab at it (now with actual code snippets rather than mocked up hypotheticals - You even get to see my crappy method stubs):
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方法和其他方法被错误地放置,并且原型链上的后代项目不可用?
I should be quite clear and say that it's not quite working yet, but mostly I'd like your opinion on whether I'm even on the right track. You mentioned "instance-specific properties and methods" in a comment in your example. Does that mean that my this.setText method and others are wrongly placed, and won't be available to descendant items on the prototype chain?
此外,使用时,它似乎声明的顺序现在很重要(我无法访问CS.Displayable.prototype,因为(我认为)CS.Button首先列出,而CS.Displayable在我尝试引用它时是未定义的) 。这是我必须要做的事情(在代码而不是我的OCD字母顺序中按照祖先的顺序排列)或者是否有我在那里俯瞰的东西?
Also, when used, it seems that the order of declaration now matters (I can't access CS.Displayable.prototype, because (I think) CS.Button is listed first, and CS.Displayable is undefined at the time that I'm trying to reference it). Is that something I'll just have to man up and deal with (put things in order of ancestry in the code rather than my OCD alphabetical order) or is there something I'm overlooking there as well?
推荐答案
Vehicles.Airplane = Object.create(Vehicles.Vehicle());
该行错误。你似乎想要使用 new Vehicles.Vehicle
- 永远不要在没有 new
的情况下调用构造函数!
That line is wrong. You seem to want to use new Vehicles.Vehicle
- never call a constructor without new
!
但是,我不确定你想要使用哪种模式。我想到了两个:
Still, I'm not sure which pattern you want to use. Two are coming to my mind:
您正在使用构造函数,就像在标准JS中一样。通过从彼此继承原型对象并在子实例上应用父构造函数来完成继承。您的代码应如下所示:
You are using constructor functions just as in standard JS. Inheritance is done by inheriting the prototype objects from each other, and applying the parent constructor on child instances. Your code should then look like this:
Vehicles.Vehicle = function () {
// instance-specific properties and methods,
// initialising
}
Vehicles.Vehicle.prototype.go = function () {
//go forwards
};
Vehicles.Vehicle.prototype.stop = function () {
//stop
};
Vehicles.Airplane = function() {
// Vehicles.Vehicle.apply(this, arguments);
// not needed here as "Vehicle" is empty
// maybe airplane-spefic instance initialisation
}
Vehicles.Airplane.prototype = Object.create(Vehicles.Vehicle.prototype, {
constructor: {value:Vehicles.Airplane, configurable:true}
}); // inheriting from Vehicle prototype, and overwriting constructor property
Vehicles.Airplane.prototype.takeOff = function () {
//take off stuff
};
// usage:
var airplane = new Vehicles.Airplace(params);
Pure Prototypical Pattern
您正在使用普通对象而不是构造函数 - 没有初始化。要创建实例和设置继承,只使用 Object.create
。它就像只有原型对象和空构造函数。 instancof
在这里不起作用。代码如下所示:
Pure Prototypical Pattern
You are using plain objects instead of constructor functions - no initialisation. To create instances, and to set up inheritance, only Object.create
is used. It is like having only the prototype objects, and empty constructors. instancof
does not work here. The code would look like this:
Vehicles.Vehicle = {
go: function () {
//go forwards
},
stop: function () {
//stop
}
}; // just an object literal
Vehicles.Airplane = Object.create(Vehicles.Vehicle); // a new object inheriting the go & stop methods
Vehicles.Airplane.takeOff = function () {
//take off stuff
};
// usage:
var airplane = Object.create(Vehicles.Airplane);
airplane.prop = params; // maybe also an "init" function, but that seems weird to me
这篇关于克罗克福德的原型继承 - 用法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!