web-dev-qa-db-fra.com

Super dans l'épine dorsale

Lorsque je remplace la méthode clone() d'un Backbone.Model, existe-t-il un moyen d'appeler cette méthode prioritaire depuis mon implantation? Quelque chose comme ça:

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

Vous voudrez utiliser:

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

Cela appellera la méthode clone() d'origine de Backbone.Model Avec le contexte this (le modèle actuel).

De Backbone docs :

Bref sur super: JavaScript ne fournit pas un moyen simple d'appeler super - la fonction du même nom définie plus haut sur la chaîne du prototype. Si vous remplacez une fonction principale comme set ou save et que vous souhaitez appeler 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);
 ...
 }    
});
99
soldier.moth

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

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

Josh Nielsen a trouvé une solution élégante pour cela , qui cache beaucoup de 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);
    }
});
18
Dave Cadwallader

En utilisant les réponses données par geek_dave et charlysisto, j'ai écrit ceci pour ajouter la prise en charge de this._super(funcName, ...) dans les classes qui ont 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é quelques bugs et accéléré les choses. 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 donné 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'elle obtienne un succès dans la classe A. D'autres solutions ne le font pas.

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

4
mab

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

Mieux vaut utiliser ce plugin: https://github.com/lukasolson/Backbone-Super

3
Roman Krom

Je pense que vous pouvez mettre en cache la méthode d'origine (bien que non testée):

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

Dans le cas où vous ne savez pas exactement ce qu'est la classe parente (héritage multiple ou si vous voulez une fonction d'assistance), 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 d'assistance:

function parent(instance) {
  return instance.__proto__.constructor.__super__;
};

var ChildModel = ParentModel.extend({

  initialize: function() {
    parent(this).initialize.apply(this, arguments);
    // Do child model initialization.
  }

});
1
Nathan Hadzariga

backbone._super.js, de mes gists: 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;
}));
1
sarink

2 fonctions ci-dessous, l'une vous demande de saisir le nom de la fonction, l'autre peut "découvrir" de 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);
    },
});
0
Alan

Passez la classe parent en option lors de 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 parent comme celle-ci

this.options.parent.method (); Gardez à l'esprit que cela crée un cycle de rétention sur les deux objets. Donc, pour laisser le garbage collector faire son travail, vous devez détruire manuellement la rétention sur l'un des objets lorsque vous en avez terminé. Si votre application est assez grande. Je vous encourage à vous pencher davantage sur les configurations hiérarchiques afin que les événements puissent se déplacer jusqu'à l'objet correct.

0
Blaine Kasten