Супер в позвоночнике
когда я переопределить clone()
метод a Backbone.Model
, есть ли способ вызвать этот переопределенный метод из моей имплантации? Что-то вроде этого:
var MyModel = Backbone.Model.extend({
clone: function(){
super.clone();//calling the original clone method
}
})
11 ответов:
вы хотите использовать:
Backbone.Model.prototype.clone.call(this);
это вызовет оригинал
clone()
методBackbone.Model
в контекстеthis
(текущая модель).кратко в сторону на супер: JavaScript не обеспечивает простой способ вызова super-одноименная функция, определенная выше на прототипе цепь. Если вы переопределяете основную функцию, такую как set или save, и вы хотите чтобы вызвать родительский объект реализации, вам придется вызвать его явно.
var Note = Backbone.Model.extend({ set: function(attributes, options) { Backbone.Model.prototype.set.apply(this, arguments); ... } });
вы также можете использовать
__super__
свойство, которое является ссылкой на родительский прототип класса:var MyModel = Backbone.Model.extend({ clone: function(){ MyModel.__super__.clone.call(this); } });
Джош Нильсен нашел элегантное решение для этого, который скрывает много уродства.
просто добавьте этот фрагмент в свое приложение, чтобы расширить модель позвоночника:
Backbone.Model.prototype._super = function(funcName){ return this.constructor.prototype[funcName].apply(this, _.rest(arguments)); }
тогда используйте его так:
Model = Backbone.model.extend({ set: function(arg){ // your code here // call the super class function this._super('set', arg); } });
Если вы хотите просто назвать это._super(); без передачи имени функции в качестве аргумента
Backbone.Controller.prototype._super = function(){ var fn = Backbone.Controller.prototype._super.caller, funcName; $.each(this, function (propName, prop) { if (prop == fn) { funcName = propName; } }); return this.constructor.__super__[funcName].apply(this, _.rest(arguments)); }
лучше использовать этот плагин: https://github.com/lukasolson/Backbone-Super
работая с ответами, данными geek_dave и charlysisto, я написал это, чтобы добавить
this._super(funcName, ...)
поддержка в классах, которые имеют несколько уровней наследования. Это хорошо работает в моем коде.Backbone.View.prototype._super = Backbone.Model.prototype._super = function(funcName) { // Find the scope of the caller. var scope = null; var scan = this.__proto__; search: while (scope == null && scan != null) { var names = Object.getOwnPropertyNames(scan); for (var i = 0; i < names.length; i++) { if (scan[names[i]] === arguments.callee.caller) { scope = scan; break search; } } scan = scan.constructor.__super__; } return scan.constructor.__super__[funcName].apply(this, _.rest(arguments)); };
через год я исправил некоторые ошибки и сделал все быстрее. Ниже приведен код, который я использую сейчас.
var superCache = {}; // Hack "super" functionality into backbone. Backbone.View.prototype._superFn = Backbone.Model.prototype._superFn = function(funcName, _caller) { var caller = _caller == null ? arguments.callee.caller : _caller; // Find the scope of the caller. var scope = null; var scan = this.__proto__; var className = scan.constructor.className; if (className != null) { var result = superCache[className + ":" + funcName]; if (result != null) { for (var i = 0; i < result.length; i++) { if (result[i].caller === caller) { return result[i].fn; } } } } search: while (scope == null && scan != null) { var names = Object.getOwnPropertyNames(scan); for (var i = 0; i < names.length; i++) { if (scan[names[i]] === caller) { scope = scan; break search; } } scan = scan.constructor.__super__; } var result = scan.constructor.__super__[funcName]; if (className != null) { var entry = superCache[className + ":" + funcName]; if (entry == null) { entry = []; superCache[className + ":" + funcName] = entry; } entry.push({ caller: caller, fn: result }); } return result; }; Backbone.View.prototype._super = Backbone.Model.prototype._super = function(funcName) { var args = new Array(arguments.length - 1); for (var i = 0; i < args.length; i++) { args[i] = arguments[i + 1]; } return this._superFn(funcName, arguments.callee.caller).apply(this, args); };
затем дается этот код:
var A = Backbone.Model.extend({ // className: "A", go1: function() { console.log("A1"); }, go2: function() { console.log("A2"); }, }); var B = A.extend({ // className: "B", go2: function() { this._super("go2"); console.log("B2"); }, }); var C = B.extend({ // className: "C", go1: function() { this._super("go1"); console.log("C1"); }, go2: function() { this._super("go2"); console.log("C2"); } }); var c = new C(); c.go1(); c.go2();
вывод в консоль это:
A1 C1 A2 B2 C2
что интересно, это вызов класса C
this._super("go1")
сканирует иерархию классов, пока она не попадет в класс A. другие решения этого не делают.П. С. раскомментировать
className
записи определений классов для включения кэширования_super
поиск. (Предполагается, что эти имена классов будут уникальными в применении.)
Я считаю, что вы можете кэшировать исходный метод (хотя и не тестировался):
var MyModel = Backbone.Model.extend({ origclone: Backbone.Model.clone, clone: function(){ origclone();//calling the original clone method } });
костяк._превосходный.js, от моей сути:https://gist.github.com/sarink/a3cf3f08c17691395edf
// Forked/modified from: https://gist.github.com/maxbrunsfeld/1542120 // This method gives you an easier way of calling super when you're using Backbone in plain javascript. // It lets you avoid writing the constructor's name multiple times. // You still have to specify the name of the method. // // So, instead of having to write: // // var Animal = Backbone.Model.extend({ // word: "", // say: function() { // return "I say " + this.word; // } // }); // var Cow = Animal.extend({ // word: "moo", // say: function() { // return Animal.prototype.say.apply(this, arguments) + "!!!" // } // }); // // // You get to write: // // var Animal = Backbone.Model.extend({ // word: "", // say: function() { // return "I say " + this.word; // } // }); // var Cow = Animal.extend({ // word: "moo", // say: function() { // return this._super("say", arguments) + "!!!" // } // }); (function(root, factory) { if (typeof define === "function" && define.amd) { define(["underscore", "backbone"], function(_, Backbone) { return factory(_, Backbone); }); } else if (typeof exports !== "undefined") { var _ = require("underscore"); var Backbone = require("backbone"); module.exports = factory(_, Backbone); } else { factory(root._, root.Backbone); } }(this, function(_, Backbone) { "use strict"; // Finds the next object up the prototype chain that has a different implementation of the method. var findSuper = function(methodName, childObject) { var object = childObject; while (object[methodName] === childObject[methodName]) { object = object.constructor.__super__; } return object; }; var _super = function(methodName) { // Keep track of how far up the prototype chain we have traversed, in order to handle nested calls to `_super`. this.__superCallObjects__ || (this.__superCallObjects__ = {}); var currentObject = this.__superCallObjects__[methodName] || this; var parentObject = findSuper(methodName, currentObject); this.__superCallObjects__[methodName] = parentObject; // If `methodName` is a function, call it with `this` as the context and `args` as the arguments, if it's an object, simply return it. var args = _.tail(arguments); var result = (_.isFunction(parentObject[methodName])) ? parentObject[methodName].apply(this, args) : parentObject[methodName]; delete this.__superCallObjects__[methodName]; return result; }; // Mix in to Backbone classes _.each(["Model", "Collection", "View", "Router"], function(klass) { Backbone[klass].prototype._super = _super; }); return Backbone; }));
в случае, если вы не знаете, что родительский класс-это точно (множественное наследование или вы хотите вспомогательную функцию), то вы можете использовать следующее:
var ChildModel = ParentModel.extend({ initialize: function() { this.__proto__.constructor.__super__.initialize.apply(this, arguments); // Do child model initialization. } });
С вспомогательной функцией:
function parent(instance) { return instance.__proto__.constructor.__super__; }; var ChildModel = ParentModel.extend({ initialize: function() { parent(this).initialize.apply(this, arguments); // Do child model initialization. } });
передать родительский класс в качестве опции во время создания экземпляра:
BaseModel = Backbone.Model.extend({ initialize: function(attributes, options) { var self = this; this.myModel = new MyModel({parent: self}); } });
тогда в вашей MyModel вы можете вызвать родительские методы, как это
этого.опции.родитель.метод(); Имейте в виду, что это создает цикл сохранения на двух объектах. Поэтому, чтобы позволить сборщику мусора делать свою работу, вам нужно будет вручную уничтожить сохранение на одном из объектов, когда закончите с ним. Если вы приложение довольно большое. Я бы посоветовал вам больше смотреть на иерархические настройки, поэтому события могут перемещаться до нужного объекта.
2 функции ниже, один требует, чтобы вы передали имя функции, другой может "обнаружить", какую функцию мы хотим супер версии
Discover.Model = Backbone.Model.extend({ _super:function(func) { var proto = this.constructor.__super__; if (_.isUndefined(proto[func])) { throw "Invalid super method: " + func + " does not exist in prototype chain."; } return proto[func].apply(this, _.rest(arguments)); }, _superElegant:function() { t = arguments; var proto = this.constructor.__super__; var name; for (name in this) { if (this[name] === arguments.callee.caller) { console.log("FOUND IT " + name); break; } else { console.log("NOT IT " + name); } } if (_.isUndefined(proto[name])) { throw "Super method for: " + name + " does not exist."; } else { console.log("Super method for: " + name + " does exist!"); } return proto[name].apply(this, arguments); }, });