理解_.bind的代码

时间:2011-12-18 16:00:17

标签: javascript underscore.js

这是_.bind的代码,取自Underscore library。我不了解采用空函数,更改原型等的业务。

  var ctor = function(){};

  _.bind = function bind(func, context) {
    var bound, args;
    if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
    if (!_.isFunction(func)) throw new TypeError;
    args = slice.call(arguments, 2);
    return bound = function() {
      if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
      ctor.prototype = func.prototype;
      var self = new ctor;
      var result = func.apply(self, args.concat(slice.call(arguments)));
      if (Object(result) === result) return result;
      return self;
    };
  };

1 个答案:

答案 0 :(得分:8)

它基本上就是这样,如果你调用new bound(),你就可以绑定new调用的参数。 E.g:

var func = function(a, b) {
    this.a = a;
    this.b = b;
};

// context does not matter, use e.g. `null`
var bound = _.bind(func, null, 1);

new bound(2); // a = 1, b = 2

我试着解释一下代码。

var ctor = function(){};

_.bind = function bind(func, context) {
    var bound, args;

    // use native .bind if available
    if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));

    // abort if not called with a function
    if (!_.isFunction(func)) throw new TypeError;

    // you can also bind arguments, which come after the function and context (so .slice(2))
    args = slice.call(arguments, 2);

    // return the bound function
    return bound = function() {

        // if you simply do bound(), then `this` is the global object.
        // This means the original function should be called with the
        // bound `this` value and arguments. Arguments you pass to the
        // bound function are concatenated to the bound arguments.
        if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));

        // otherwise, you're calling like `new bound()`, because `this instanceof bound`.
        // In that case, `this` should not be passed, but only the arguments. So, you
        // create a function of which the prototype is the original function's prototype,
        // and create an instance of it (to mimic `new func`).
        ctor.prototype = func.prototype;

        var self = new ctor; // won't execute anything since ctor's body is empty.
                             // Just creates an instance

        // then, you call the original function with the `this` value of the instance,
        // with bound arguments and new arguments concatenated. This way, the constructor
        // (func) is executed on the instance.
        var result = func.apply(self, args.concat(slice.call(arguments)));

        // finally, return the result if it's an object (the specs say `new xxx` should
        // return an object), otherwise return the instance (like you would with `new func`)
        if (Object(result) === result) return result;
        return self;
    };
};