Является ли этот образец паразитического наследования хорошим? - PullRequest
2 голосов
/ 17 ноября 2011

Создание прототипного объекта в 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 Ответ

2 голосов
/ 17 ноября 2011

Нет, вы не от цели.Но ты тоже не изобрел это колесо.Этот тип наследования ECMAscript стал очень известным благодаря книге Дуга Крокфордса Javascript: The good parts.

. Это хороший шаблон, в котором используются замыкания, которые обеспечивают конфиденциальность и защиту вещей.Тем не менее, все зависит от вас, какие шаблоны вы предпочитаете (простое наследование прототипов, псевдоклассика).

С ES5 и новыми возможностями, такими как Object.create() и Object.defineProperties(), Object.freeze(), чтобы назвать несколько, мытакже есть хорошие способы защиты и конфиденциальности с более прототипным подходом.Лично я все еще предпочитаю и люблю псевдоклассический способ, использующий замыкания для выполнения чего-либо.

Предостережение может по-прежнему вызывать служебные вызовы функций, которые обычно можно избежать, используя простое наследование прототипов.Нам нужно сделать гораздо больше звонков, чтобы все было сделано правильно (если дела растут).Тем не менее, закрытия считаются немного жадными в памяти и, вероятно, причиной утечек, если мы используем их небрежно или забываем очищать ссылки здесь и там.Сейчас у меня нет никаких ссылок на это, но я твердо верю, что последние движки js не намного медленнее используют закрытие как много.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...