这种寄生遗传模式有什么好处吗?

时间:2011-11-17 09:56:48

标签: javascript design-patterns

声称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.

好的,我应该问一个问题。我完全偏离了这里的目标还是我的某些东西?有哪些明显的缺点?

1 个答案:

答案 0 :(得分:2)

不,你没有偏离目标。但你也没有发明那个轮子。这种类型的ECMAscript继承因Doug Crockfords的书Javascript: The good parts而变得非常有名。

这是一个很好的模式,很好地使用闭包来保持私密和受保护。但是,您仍然可以选择您喜欢的模式(纯原型继承,伪古典)。

使用ES5以及Object.create()Object.defineProperties()Object.freeze()这样的新可能性,我们也有很好的方法可以通过更原型的方法获得保护和隐私。就个人而言,我仍然喜欢并喜欢伪古典方式,使用闭包来做事。

警告仍然可能是函数调用开销,您通常可以避免使用简单的原型继承。我们需要做更多的调用才能正确完成事情(如果事情增长)。尽管如此,闭包被认为是一点点内存贪婪,如果我们使用它们草率或忘记清理引用,可能是泄漏的原因。我现在没有任何参考,但我坚信最新的js引擎使用类似的闭包并没有太慢。