声称JavaScript中的原型对象创建功能强大(我听说它很有效,如果使用得当,非常富有表现力)。但出于某种原因,我发现它比我帮助我更频繁地绊倒我。
我对涉及原型的对象创建模式的主要问题是无法绕过对this
的需求。主要原因是任何超出原始状态的对象,例如通过异步API调用填充自身的对象this
由于范围的变化而中断。
所以,我使用原型对象创建对象,我从一开始就知道一切。
但对于需要执行API调用以保持自己最新的对象,我完全跳过原型并使用直接对象文字。
当我觉得需要扩展其中一个对象时,我使用了寄生继承:
var ROOT = ROOT || {};
ROOT.Parent = function () {
var self = {
func1 : function () {
alert("func1")
};
}
return self;
};
ROOT.Child = function () {
var self = ROOT.Parent(); // This is the parasitizing
self.func2 = function () {
alert("func2")
};
return self;
};
var myChild = ROOT.Child();
myChild.func1(); // alerts "func1"
myChild.func2(); // alerts "func2"
使用此模式,我可以在func1
对象中重用ROOT.Child
的代码。但是,如果我想扩展func1
中的代码,我会遇到问题。即如果我想在父母func1
和我自己的func1
中调用代码,这种模式会带来挑战。我不能这样做:
ROOT.Child = function () {
var self = ROOT.Parent();
self.func1 = function () {
alert("func2")
};
};
由于这将完全取代该功能。为了解决这个问题,我提出了以下解决方案(您也可以在这里查看:http://jsfiddle.net/pellepim/mAGUg/9/)。
var ROOT = {};
/**
* This is the base function for Parasitic Inheritence
*/
ROOT.Inheritable = function () {
var self = {
/**
* takes the name of a function that should exist on "self", and
* rewires it so that it executes both the original function, and the method
* supplied as second parameter.
*/
extend : function (functionName, func) {
if (self.hasOwnProperty(functionName)) {
var superFunction = self[functionName];
self[functionName] = function () {
superFunction();
func();
};
}
},
/**
* Takes the name of a function and reassigns it to the function supplied
* as second parameter.
*/
replace : function (methodName, func) {
self[methodName] = func;
}
};
return self;
};
/**
* "Inherits" from ROOT.Inheritable
*/
ROOT.Action = function () {
var self = ROOT.Inheritable();
/**
* I intend to extend this method in an inheriting object
*/
self.methodToExtend = function () {
alert("I should be seen first, since I get extended");
};
/**
* I intend to replace this method in an inheriting object
*/
self.methodToReplace = function () {
alert("I should never be seen, since I get replaced.");
};
return self;
};
/**
* "Inherits" from ROOT.Action.
*/
ROOT.Task = function () {
var self = ROOT.Action();
self.extend('methodToExtend', function () {
alert("I successfully ran the extended code too.");
});
/**
* I know it is completely unecessary to have a replace method,
* I could just as easily just type self.methodToReplace = function () ...
* but I like that you see that you are actually replacing something.
*/
self.replace('methodToReplace', function () {
alert("I successfully replaced the \"super\" method.");
});
return self;
};
var task = ROOT.Task();
task.methodToExtend(); // I expect both the "base" and "child" method to run.
task.methodToReplace(); // I expect only the "child" method to run.
好的,我应该问一个问题。我完全偏离了这里的目标还是我的某些东西?有哪些明显的缺点?
答案 0 :(得分:2)
不,你没有偏离目标。但你也没有发明那个轮子。这种类型的ECMAscript继承因Doug Crockfords的书Javascript: The good parts
而变得非常有名。
这是一个很好的模式,很好地使用闭包来保持私密和受保护。但是,您仍然可以选择您喜欢的模式(纯原型继承,伪古典)。
使用ES5以及Object.create()
和Object.defineProperties()
,Object.freeze()
这样的新可能性,我们也有很好的方法可以通过更原型的方法获得保护和隐私。就个人而言,我仍然喜欢并喜欢伪古典方式,使用闭包来做事。
警告仍然可能是函数调用开销,您通常可以避免使用简单的原型继承。我们需要做更多的调用才能正确完成事情(如果事情增长)。尽管如此,闭包被认为是一点点内存贪婪,如果我们使用它们草率或忘记清理引用,可能是泄漏的原因。我现在没有任何参考,但我坚信最新的js引擎使用类似的闭包并没有太慢。