Создание прототипного объекта в 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.
Хорошо, поэтому я должен задать вопрос. Я полностью сошел с цели или я к чему-то? Каковы очевидные недостатки?