克罗克福德式原型模式明白了;寻找一个优雅的解决方案
Crockford-style prototypal pattern gotcha; looking for an elegant solution
我在编写JavaScript程序时经常使用Crockford的原型模式。我以为我理解了所有的"陷阱",但我发现了一个我以前没有想到的问题。我想知道是否有人有处理它的最佳实践。
下面是一个简单的例子:
// Here's the parent object
var MyObject = {
registry: {},
flatAttribute: null,
create: function () {
var o, F = function () {};
F.prototype = this;
o = new F();
return o;
}
};
// instance is an empty object that inherits
// from MyObject
var instance = MyObject.create();
// Attributes can be set on instance without modifying MyObject
instance.flatAttribute = "This is going to be applied to the instance";
// registry doesn't exist on instance, but it exists on
// instance.prototype. MyObject's registry attribute gets
// dug up the prototype chain and altered. It's not possible
// to tell that's happening just by examining this line.
instance.registry.newAttribute = "This is going to be applied to the prototype";
// Inspecting the parent object
// prints "null"
console.log(MyObject.flatAttribute);
// prints "This is going to be applied to the prototype"
console.log(MyObject.registry.newAttribute);
我想确保对实例所做的任何更改都不会向上传播到继承更改。当属性是一个对象并且我正在设置一个嵌套属性时,情况就不是这样了。
解决方案是重新初始化实例上的所有对象属性。然而,使用这种模式的优点之一是从构造函数中删除了重新初始化代码。我正在考虑克隆父类的所有对象属性,并在create()函数的实例中设置它们:
{ create: function () {
var o, a, F = function () {};
F.prototype = this;
o = new F();
for (a in this) {
if (this.hasOwnProperty(a) && typeof this[a] === 'object') {
// obviously deepclone would need to be implemented
o[a] = deepclone(this[a]);
}
}
return o;
} };
有更好的方法吗?
要确保它们只是实例变量,有一个非常简单的解决方案,那就是在构造函数中使用this关键字。
var MyObject = {
flatAttribute: null,
create: function () {
var o, F = function () {
this.registry = {}
};
F.prototype = this;
o = new F();
return o;
}
};
确保"instance.registry. . "的所有属性。*"是本地实例,因为javascript对象的查找顺序如下:
object -> prototype -> parent prototype ...
因此,通过在构造函数中添加一个名为"registry"的变量到实例中,该变量总是首先被找到。
另一个解决方案,我认为更优雅的是不使用crockford的(java风格)构造函数,而使用更自然地反映javascript对象系统的布局。这些陷阱大多来自于实践和语言之间的不匹配。
// instance stuff
var F = function () {
this.registry = {}
};
F.prototype = {
// static attributes here
flatAttribute: null,
methodA: function(){
// code here 'this' is instance object
this.att = 'blah';
}
};
var instanceA = new F();
instanceA.registry['A'] = 'hi';
var instanceB = new F();
instanceB.registry['B'] = 'hello';
instanceA.registry.A == 'hi'; // true
instanceB.registry.B == 'hello'; // true
F.prototype.registry == undefined; // true
这会给你预期的结果吗?这里我没有使用Object字面量,而是使用父对象(Base)的即时实例化构造函数:
var Base = ( function(){
function MyObject(){
this.registry = {},
this.flatAttribute = null;
if (!MyObject.prototype.create)
MyObject.prototype.create = function(){
return new this.constructor();
};
}
return new MyObject;
} )(),
// create 2 instances from Base
instance1 = Base.create(),
instance2 = Base.create();
// assign a property to instance1.registry
instance1.registry.something = 'blabla';
// do the instance properties really belong to the instance?
console.log(instance1.registry.something); //=> 'blabla'
console.log(instance2.registry.something === undefined); //=> true
但这一切都有点虚拟。如果您不想使用new
操作符(我认为这就是它的全部思想),下面为您提供了一种不需要创建方法的方法:
function Base2(){
if (!(this instanceof Base2)){
return new Base2;
}
this.registry = {},
this.flatAttribute = null;
if (!Base2.prototype.someMethod){
var proto = Base2.prototype;
proto.someMethod = function(){};
//...etc
}
}
//now the following does the same as before:
var instance1 = Base2(),
instance2 = Base2();
// assign a property to instance1.registry
instance1.registry.something = 'blabla';
// do the instance properties really belong to the instance?
console.log(instance1.registry.something); //=> 'blabla'
console.log(instance2.registry.something === undefined); //=> true
jsfiddle
我总是喜欢记住这个对象。创建是一种选择,但不是javascript中实现非经典继承的唯一方法。
对于我自己,我总是找到那个对象。当我想从父对象原型链继承元素(即我希望能够应用于继承对象的方法)时,创建工作最好。
,
对于简单的"Own Property"继承,Object。Create在很大程度上是不必要的。当我想继承自己的属性时,我更喜欢使用流行的Mixin &扩展模式(简单地将一个对象自己的属性复制到另一个对象,而不用担心原型或"new")。
在Stoyan Stefanov书"Javascript Patterns"中,他给出了一个深度扩展函数的例子,它可以递归地完成你正在寻找的东西,并且包括对数组属性以及标准键/值对象的支持:
function extendDeep(parent, child){
var i,
toStr = Object.prototype.toString,
astr = "[object Array]";
child = child || {};
for (i in parent) {
if (parent.hasOwnProperty(i)) {
if (typeof parent[i] === "object") {
child[i] = (toStr.call(parent[i]) === astr) ? [] : {};
extendDeep(parent[i], child[i]);
} else {
child[i] = parent[i];
}
}
}
return child;
}
如果你正在使用jQuery, jQuery.extend()有一个可选的"deep"参数,可以让你以几乎相同的方式扩展对象。
我想你是在使用原型继承来模拟一个经典的, 面向对象的继承。
你想做的是停止原型方法查找,这限制了它的表现力,所以为什么要使用它?您可以通过使用函数模式:
来达到相同的效果var MyObject = function() {
// Declare here shared vars
var global = "All instances shares me!";
return {
'create': function() {
var flatAttribute;
var register = {};
return {
// Declare here public getters/setters
'register': (function() {
return register;
})(),
'flatAttribute': (function() {
return flatAttribute;
})(),
'global': (function() {
return global;
})()
};
}
};
}();
var instance1 = MyObject.create();
var instance2 = MyObject.create();
instance1.register.newAttr = "This is local to instance1";
instance2.register.newAttr = "This is local to instance2";
// Print local (instance) var
console.log(instance1.register.newAttr);
console.log(instance2.register.newAttr);
// Print global var
console.log(instance1.global);
console.log(instance2.global);
jsFiddle上的代码
- iPad虚拟键盘-哪一个-javasctript解决方案
- 如何让我的网站上的WEBP图像加载在morzilla firefox中有一个可能的解决方案吗?如果是,那么如何
- 这里有一个更优雅的/DRY/可维护的JS解决方案
- 当我想发布一个变量但有 1 个 js 时,ajax post 的解决方案是什么
- 有没有一个简单的解决方案可以根据3个数据找到结果
- 将复杂内容(想想整个网页树)从一个 Web 应用程序提供给另一个网站的最佳可重用解决方案是什么?
- 需要一个类似于“另存为”对话框的解决方案来使用 javascript 选择文件名和位置
- 需要一个优化的解决方案来获取按字母顺序排列的记录集 javascript
- 在 JavaScript 中将小数字合并为一个是个好主意吗?(作为存储优化解决方案)
- 寻找一个纯粹的Javascript函数式编程解决方案
- 未捕获的类型错误: $(..).froala编辑器不是一个函数//解决方案被找到,但不确定如何使用它
- 寻找一个交互式javascript地图解决方案,允许放大,标记和使用位图图像(不是SVG))
- 什么'这是一个很好的webrtc音频解决方案
- 淡出/淡出使DIV's堆叠而不是重叠?使用& # 39;绝对# 39;这不是一个解决方案
- 如果节点模块' body-parser '不安全,下一个解决方案是什么
- 需要一个解决方案,以增加减少字体大小的网站
- 需要一个解决方案,以阻止Safari浏览器阻止Facebook身份验证弹出框,允许用户登录到我的网站
- 为什么我的解决方案有效,而另一个解决方案无效
- 是否有一个解决方案,以获得对象直接到指令范围
- 寻找一个解决方案,作为radiobutton.click()挂Safari,但在Chrome中工作