Супер в Backbone


74

Коли я замінюю clone()метод a Backbone.Model, чи є спосіб викликати цей замінений метод із моєї імплантації? Щось на зразок цього:

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

Відповіді:


100

Ви хочете використовувати:

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

Це викличе вихідний clone()метод Backbone.Modelіз контексту this(Поточна модель).

З документів Backbone :

Коротко про супер: JavaScript не забезпечує простий спосіб викликати супер - функцію з однойменною назвою, визначену вище в ланцюжку прототипів. Якщо ви перевизначаєте основну функцію, наприклад set, або зберігаєте, і ви хочете викликати реалізацію батьківського об'єкта, вам доведеться явно викликати її.

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

Документи Backbone, схоже, пропонують, наприклад, Backbone.Model.prototype.set.apply (це, аргументи); у чому різниця між використанням prototype.func_name.apply (...) та prototype.func_name.call (...)?
Mikael Lepistö

5
@ MikaelLepistö бачити питання stackoverflow.com/questions/1986896 / ...
soldier.moth

1
Це не буде працювати з повним ланцюжком прототипів. Якщо прототип прямого супер-класу не містить метод, буде виняток.
Тобіас Кудник

36

Ви також можете використовувати __super__властивість, яка є посиланням на прототип батьківського класу:

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

22
Тільки невелика довідка щодо цієї відповіді: __super__це посилання на батьківський прототип, який Backbone framework робить щоразу, коли розширюється модель, колекція, маршрутизатор або подання Backbone. Хоча це не є стандартною властивістю, воно працює в різних браузерах, оскільки його структура створена. Однак навіть офіційні документи Backbone про це не згадують і кажуть використовувати Backbone.Model.prototype.set.call(this, attributes, options);метод. Хоча обидва вони працюють нормально.
Мовіс Ледфорд,

@MauvisLedford Чи правильний ваш приклад коду чи повинен .set.бути .clone.для випадку використання OP?
MikeSchinkel

Мій - це просто не пов’язаний приклад. Це було б Backbone.Model.prototype.clone.call(this, attributes, options);у його випадку.
Мовіс Ледфорд,

2
Ви також можете використовувати: this.constructor .__ super__
Джейсон М

@JasonM ні, this.constructorне гарантовано, MyModelі це призведе до переповнення стека, якщо MyModelвін використовувався як батьківський клас.
Еміль Бержерон,

18

Джош Нільсен знайшов для цього елегантне рішення , яке приховує багато потворності.

Просто додайте цей фрагмент у свій додаток, щоб розширити модель Backbone:

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

5
це працює лише тоді, коли існує лише один рівень реалізації A.foo -> B.foo, у випадку з A.foo -> B.foo -> C.foo ви отримаєте переповнення стека завдяки тому, що B посилається на себе
monastic-panic

Моя відповідь нижче вирішує проблему наявності більш ніж одного рівня успадкування,
mab

4

Працюючи на основі відповідей, наданих 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. Інші рішення цього не роблять.

PS Розкомментируйте classNameзаписи визначень класів, щоб уможливити кешування _superпошуку. (Припущення полягає в тому, що ці назви класів будуть унікальними в додатку.)


Хотілося б, щоб я не раз проголосував за цю відповідь. Перший розумний опис супер я знайшов в Інтернеті. Ця концепція мене завжди бентежила, вона зовсім потойбічна від стандартного JavaScript ...
I_ATE_YOUR_WORK_FILES

3

Якщо ви хочете просто зателефонувати this._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


2

Я вважаю, що ви можете кешувати оригінальний метод (хоча і не перевірений):

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

Має бути Backbone.Model.prototype.cloneі this.origclone(). Це еквівалентно Backbone.Model.prototype.clone.call(this).
Еміль Бержерон,

1

backbone._super.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;
}));

1

Якщо ви не знаєте, що саме є батьківський клас (багаторазове успадкування або ви хочете допоміжну функцію), ви можете використовувати наступне:

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.
  }

});

0

Передайте батьківський клас як варіант під час створення екземпляра:

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

Тоді у вашому MyModel ви можете викликати такі батьківські методи

this.options.parent.method (); Майте на увазі, це створює цикл збереження для двох об’єктів. Отже, щоб дозволити збиральнику сміття робити це, вам потрібно буде вручну знищити утримувач одного з об’єктів, коли закінчите з ним. Якщо ви заявка досить велика. Я радив би вам детальніше вивчити ієрархічні установки, щоб події могли подорожувати до правильного об’єкта.


Це не приклад успадкування і не той самий "батько", який обговорюється в цій темі. Крім того, var self = thisнепотрібне в цьому випадку, оскільки воно не перебуває всередині функції зворотного виклику, що втрачає контекст.
Еміль Бержерон,

1
Правда. Я написав цей код, коли був молодим і німим. Дякуємо за оновлення тут. Не соромтеся редагувати публікацію та оновлювати її. Я більше не пишу Backbone, тому не відчуваю, що можу впевнено оновити його.
Блейн Кастен

Його редагування не допоможе, оскільки це не відповідь на відповідне запитання. Думаю, єдиним варіантом буде взагалі видалити відповідь.
Еміль Бержерон,

0

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

-1

Ось як я це зробив:

ParentClassName.prototype.MethodToInvokeName.apply(this);

так для вашого прикладу це:

Model.prototype.clone.apply(this)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.