Javascript 超级骨干

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8596861/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 06:39:20  来源:igfitidea点击:

Super in Backbone

javascriptinheritancebackbone.js

提问by Andreas K?berle

When I override the clone()method of a Backbone.Model, is there a way to call this overriden method from my implantation? Something like this:

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

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

回答by soldier.moth

You'll want to use:

你会想要使用:

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

This will call the original clone()method from Backbone.Modelwith the context of this(The current model).

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

From Backbone docs:

来自Backbone 文档

Brief aside on super: JavaScript does not provide a simple way to call super — the function of the same name defined higher on the prototype chain. If you override a core function like set, or save, and you want to invoke the parent object's implementation, you'll have to explicitly call it.

简要介绍 super:JavaScript 没有提供一种简单的方法来调用 super——定义在原型链更高层的同名函数。如果您覆盖了一个核心函数,如 set 或 save,并且您想调用父对象的实现,则必须显式调用它。

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

回答by charlysisto

You can also use the __super__property which is a reference to the parent class prototype:

您还可以使用作为__super__对父类原型的引用的属性:

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

回答by Dave Cadwallader

Josh Nielsen found an elegant solution for this, which hides a lot of the ugliness.

Josh Nielsen为此找到了一个优雅的解决方案,它隐藏了很多丑陋之处。

Just add this snippet to your app to extend Backbone's model:

只需将此代码段添加到您的应用程序即可扩展 Backbone 的模型:

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

Then use it like this:

然后像这样使用它:

Model = Backbone.model.extend({
    set: function(arg){
        // your code here

        // call the super class function
        this._super('set', arg);
    }
});

回答by mab

Working from the answers given by geek_dave and charlysisto, I wrote this to add this._super(funcName, ...)support in classes that have multiple levels of inheritance. It's worked well in my code.

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

A year later I've fixed some bugs and made things faster. Below is the code that I use now.

一年后,我修复了一些错误并使事情变得更快。下面是我现在使用的代码。

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

Then given this 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();

The output in the console is this:

控制台中的输出是这样的:

A1
C1
A2
B2
C2

What's interesting is that class C's call to this._super("go1")scans the class hierarchy until it gets a hit in class A. Other solutions do not do this.

有趣的是,类 C 调用this._super("go1")扫描类层次结构,直到它在类 A 中被命中。其他解决方案不这样做。

P.S. Uncomment the classNameentries of the class definitions to enable caching of the _superlookup. (The assumption is that these class names will be unique in the application.)

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

回答by Roman Krom

If you want just to call this._super(); without passing the function name as an argument

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

Better use this plugin: https://github.com/lukasolson/Backbone-Super

最好使用这个插件:https: //github.com/lukasolson/Backbone-Super

回答by swatkins

I believe you can cache the original method (although not tested):

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

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

回答by sarink

backbone._super.js, from my gists: https://gist.github.com/sarink/a3cf3f08c17691395edf

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

回答by Nathan Hadzariga

In the case that you don't know what the parent class is exactly (multiple inheritance or you want a helper function) then you can use the following:

如果您不知道父类究竟是什么(多重继承或您想要一个辅助函数),那么您可以使用以下内容:

var ChildModel = ParentModel.extend({

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

});

With helper function:

带辅助功能:

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

var ChildModel = ParentModel.extend({

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

});

回答by Alan

2 functions below, one requires you pass in the function name, the other can "discover" which function we want the super version of

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

回答by Blaine Kasten

Pass the parent class as an option during instantiation:

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

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

Then in your MyModel you can call parent methods like this

然后在你的 MyModel 你可以像这样调用父方法

this.options.parent.method(); Keep in mind this creates a retain cycle on the two objects. So to let the garbage collector do it's job you would need to manually destroy the retain on one of the objects when finished with it. If you're application is pretty large. I would encourage you to look more into hierarchal setups so events can travel up to the correct object.

this.options.parent.method(); 请记住,这会在两个对象上创建一个保留循环。因此,为了让垃圾收集器完成它的工作,您需要在完成后手动销毁其中一个对象上的保留。如果您的应用程序非常大。我鼓励您更多地研究分层设置,以便事件可以传播到正确的对象。