Super dans l'épine dorsale

Lorsque je remplace la méthode clone() d'un Backbone.Model , est-il possible d'appeler cette méthode overriden de mon implantation? Quelque chose comme ça:

 var MyModel = Backbone.Model.extend({ clone: function(){ super.clone();//calling the original clone method } }) 

Vous voudrez utiliser:

 Backbone.Model.prototype.clone.call(this); 

Cela appellera la méthode clone() originale de Backbone.Model avec le contexte de this (Le modèle actuel).

À partir des documents Backbone :

Sommaire sur super: JavaScript ne fournit pas un moyen simple d'appeler super – la fonction du même nom défini plus haut sur la chaîne prototype. Si vous annulez une fonction principale comme set ou save, et que vous souhaitez invoquer l'implémentation de l'objet parent, vous devrez l'appeler explicitement.

 var Note = Backbone.Model.extend({ set: function(attributes, options) { Backbone.Model.prototype.set.apply(this, arguments); ... } }); 

Vous pouvez également utiliser la propriété __super__ qui est une référence au prototype de classe parent:

 var MyModel = Backbone.Model.extend({ clone: function(){ MyModel.__super__.clone.call(this); } }); 

Josh Nielsen a trouvé une solution élégante pour cela , ce qui cache beaucoup la laideur.

Ajoutez simplement cet extrait à votre application pour étendre le modèle de Backbone:

 Backbone.Model.prototype._super = function(funcName){ return this.constructor.prototype[funcName].apply(this, _.rest(arguments)); } 

Ensuite, utilisez-le comme ceci:

 Model = Backbone.model.extend({ set: function(arg){ // your code here // call the super class function this._super('set', arg); } }); 

Si vous voulez simplement appeler this._super (); Sans passer le nom de la fonction en argument

 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)); } 

Utilisez mieux ce plugin: https://github.com/lukasolson/Backbone-Super

En travaillant à partir des réponses fournies par geek_dave et charlysisto, j'ai écrit ceci pour ajouter le this._super(funcName, ...) dans les classes ayant plusieurs niveaux d'héritage. Cela a bien fonctionné dans mon code.

 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)); }; 

Un an plus tard, j'ai corrigé certains bogues et rendu plus rapide. Voici le code que j'utilise maintenant.

 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); }; 

Puis, donne ce code:

 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(); 

La sortie dans la console est la suivante:

 A1 C1 A2 B2 C2 

Ce qui est intéressant, c'est que l'appel de la classe C à this._super("go1") analyse la hiérarchie des classes jusqu'à ce qu'il obtienne un coup dans la classe A. D'autres solutions ne le font pas.

PS Décomment les entrées className des définitions de classe pour permettre la mise en cache de la recherche _super . (L'hypothèse est que ces noms de classe seront uniques dans l'application.)

Je crois que vous pouvez mettre en cache la méthode d'origine (bien que pas testé):

 var MyModel = Backbone.Model.extend({ origclone: Backbone.Model.clone, clone: function(){ origclone();//calling the original clone method } }); 

Backbone._super.js, à partir de mes notes: 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; })); 

Dans le cas où vous ne savez pas ce que la classe parent est exactement (héritage multiple ou vous voulez une fonction helper), vous pouvez utiliser ce qui suit:

 var ChildModel = ParentModel.extend({ initialize: function() { this.__proto__.constructor.__super__.initialize.apply(this, arguments); // Do child model initialization. } }); 

Avec fonction helper:

 function parent(instance) { return instance.__proto__.constructor.__super__; }; var ChildModel = ParentModel.extend({ initialize: function() { parent(this).initialize.apply(this, arguments); // Do child model initialization. } }); 

Passez la classe parent comme option pendant l'instanciation:

 BaseModel = Backbone.Model.extend({ initialize: function(attributes, options) { var self = this; this.myModel = new MyModel({parent: self}); } }); 

Ensuite, dans votre MyModel, vous pouvez appeler des méthodes parentales comme celle-ci

This.options.parent.method (); Gardez à l'esprit que cela crée un cycle de retenue sur les deux objets. Donc, pour laisser le collecteur d'ordures faire son travail, vous devrez détruire manuellement le retenue sur l'un des objets lorsque vous l'avez fini. Si vous êtes une application, c'est assez grand. Je vous encourage à regarder plus dans les configurations hiérarchiques afin que les événements puissent voyager jusqu'à l'objet correct.

2 fonctions ci-dessous, on exige que vous passiez le nom de la fonction, l'autre peut "découvrir" quelle fonction nous voulons la super version de

 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); }, }); 

Voici comment je ferais ceci:

 ParentClassName.prototype.MethodToInvokeName.apply(this); 

Donc, pour votre exemple, ceci est:

 Model.prototype.clone.apply(this)