Супер в 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.