[英]Crockford's Prototypical Inheritance - Usage
我一直在构建一个小型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对象可以去()和停止(),但我想要更多。 我想将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未定义我试图引用它)。 这是我必须要做的事情(在代码而不是我的OCD字母顺序中按照祖先的顺序排列)或者是否有我在那里俯瞰的东西?
Vehicles.Airplane = Object.create(Vehicles.Vehicle());
那条线是错的。 你似乎想要使用new Vehicles.Vehicle
- 永远不要在没有new
情况下调用构造函数!
不过,我不确定你想要使用哪种模式。 我想到了两个:
您正在使用构造函数,就像在标准JS中一样。 通过继承彼此的原型对象并在子实例上应用父构造函数来完成继承。 您的代码应如下所示:
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);
您正在使用普通对象而不是构造函数 - 没有初始化。 要创建实例和设置继承,只使用Object.create
。 它就像只有原型对象和空构造函数。 instancof
在这里不起作用。 代码如下所示:
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
你的Object.create
错了。 第一个参数应该是一个对象 (也许这就是人们建议你传递文字的原因)。
在您的第一个示例中,您实际上是在传递undefined
:
Vehicles.Airplane = Object.create(Vehicles.Vehicle()); // the function call will
// return undefined
以下是可行的,但它不是非常Crockford-ish:
Vehicles.Airplane = Object.create(new Vehicles.Vehicle());
我相信克罗克福德会这样做的方式(或者,至少,不会抱怨):
var Vehicles = {};
Vehicles.Vehicle = {
go : function() {
// go stuff
},
stop : function() {
// go stuff
}
};
Vehicles.Airplane = Object.create(Vehicles.Vehicle, {
takeOff : {
value : function() {
// take-off stuff
}
},
land : {
value: function() {
// land stuff
}
}
});
请注意, Vehicles.Vehicle
只是一个文字,它将用作其他对象的原型。 当我们调用Object.create
,我们将Vehicles.Vehicle
作为原型传递, takeOff
和land
将成为Vehicles.Airplane
的属性。 然后,您可以再次调用Object.create
,如果您想创建例如波音,则将Vehicles.Airplane
作为原型。
作为第二个参数传递的自己的属性打包在一个包含其属性描述符表示的对象中。 外键是属性/方法的名称,每个键都指向包含实际实现的另一个对象作为value
。 您还可以包含其他键,如enumerable
; 如果你不这样做,他们将采用默认值。 您可以在MDN页面上阅读有关Object.defineProperty
更多有关描述符的信息 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.