【问题标题】:Super in Backbone超级骨干
【发布时间】:2012-01-25 16:19:59
【问题描述】:

当我覆盖Backbone.Modelclone() 方法时,有没有办法从我的植入中调用这个被覆盖的方法?像这样的:

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

【问题讨论】:

    标签: javascript inheritance backbone.js


    【解决方案1】:

    你会想要使用:

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

    这将使用this(当前模型)的上下文从Backbone.Model 调用原始clone() 方法。

    来自Backbone docs

    关于 super 的简要说明:JavaScript 没有提供简单的调用方式 super — 在原型上定义的同名函数 链。如果你重写了一个核心函数,比如 set 或 save,并且你想要 要调用父对象的实现,您必须 明确地调用它。

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

    【讨论】:

    • Backbone docs 似乎建议例如Backbone.Model.prototype.set.apply(this, arguments);使用prototype.func_name.apply(...) 和prototype.func_name.call(...) 有什么区别?
    • @MikaelLepistö 看到问题stackoverflow.com/questions/1986896/…
    • 这不适用于完整的原型链。如果直接超类的原型不包含该方法,则会出现异常。
    【解决方案2】:

    您也可以使用__super__ 属性,它是对父类原型的引用:

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

    【讨论】:

    • 这个答案的一点背景:__super__ 是对 Backbone 框架每次扩展 Backbone 模型、集合、路由器或视图时创建的父原型的引用。虽然它不是标准属性,但它确实可以跨浏览器工作,因为它的框架生成。但是,即使是 Backbone 官方文档也没有提到这一点,而是说要使用 Backbone.Model.prototype.set.call(this, attributes, options); 方法。不过,两者似乎都可以正常工作。
    • @MauvisLedford 您的示例代码是否正确,或者对于 OP 的用例,.set. 是否应该是 .clone.
    • 我的只是一个不相关的例子。在他的情况下是Backbone.Model.prototype.clone.call(this, attributes, options);
    • 你也可以使用:this.constructor.__super__
    • @JasonM 不,this.constructor 不保证是MyModel,如果将MyModel 用作父类,它会导致堆栈溢出。
    【解决方案3】:

    Josh Nielsen found an elegant solution for this,隐藏了很多丑陋。

    只需将此 sn-p 添加到您的应用中即可扩展 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);
        }
    });
    

    【讨论】:

    • 这仅在只有一层实现 A.foo -> B.foo 时有效,在 A.foo -> B.foo -> C.foo 的情况下,您将获得一个堆栈由于 B 的 this 引用自身而溢出
    • 我下面的回答解决了多级继承的问题,
    【解决方案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 类中被命中。其他解决方案不这样做。

    附:取消注释类定义的className 条目以启用_super 查找的缓存。 (假设这些类名在应用程序中是唯一的。)

    【讨论】:

    • 我希望我能不止一次地支持这个答案。我在互联网上找到的第一个对 super 的合理描述。这个概念总是让我感到困惑,它与标准 JavaScript 完全不同……
    【解决方案5】:

    如果你只想调用 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

    【讨论】:

      【解决方案6】:

      我相信你可以缓存原始方法(虽然没有测试):

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

      【讨论】:

      • 应该是Backbone.Model.prototype.clonethis.origclone()。相当于Backbone.Model.prototype.clone.call(this)
      【解决方案7】:

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

      【讨论】:

        【解决方案8】:

        如果你不知道父类到底是什么(多重继承或者你想要一个辅助函数),那么你可以使用以下方法:

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

        【讨论】:

          【解决方案9】:

          在实例化期间将父类作为选项传递:

          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 在这种情况下是不必要的,因为它不在丢失上下文的回调函数中。
          • 真相。我在年轻和愚蠢的时候编写了这段代码。感谢这里的更新。随意编辑帖子并更新它。我不再写 Backbone,所以我觉得我不能自信地更新它。
          • 编辑它无济于事,因为它没有回答手头的问题。我认为唯一的选择是完全删除答案。
          【解决方案10】:

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

          【讨论】:

            【解决方案11】:

            我会这样做:

            ParentClassName.prototype.MethodToInvokeName.apply(this);
            

            所以你的例子是:

            Model.prototype.clone.apply(this)
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2013-04-20
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2023-03-16
              相关资源
              最近更新 更多