124

在 Javascript 中,如何在不绑定this参数的情况下将参数绑定到函数?

例如:

//Example function.
var c = function(a, b, c, callback) {};

//Bind values 1, 2, and 3 to a, b, and c, leave callback unbound.
var b = c.bind(null, 1, 2, 3); //How can I do this without binding scope?

this我怎样才能避免必须绑定函数的范围(例如设置= null)的副作用?

编辑:

对困惑感到抱歉。我想绑定参数,然后能够稍后调用绑定函数并让它的行为就像我调用原始函数并将绑定参数传递给它一样:

var x = 'outside object';

var obj = {
  x: 'inside object',
  c: function(a, b, c, callback) {
    console.log(this.x);
  }
};

var b = obj.c.bind(null, 1, 2, 3);

//These should both have exact same output.
obj.c(1, 2, 3, function(){});
b(function(){});

//The following works, but I was hoping there was a better way:
var b = obj.c.bind(obj, 1, 2, 3); //Anyway to make it work without typing obj twice?

我还是新手,很抱歉造成混乱。

谢谢!

4

16 回答 16

33

您可以这样做,但最好避免将其视为“绑定”,因为这是用于设置“this”值的术语。也许将其视为将参数“包装”到函数中?

您所做的是创建一个函数,该函数通过闭包内置了所需的参数:

var withWrappedArguments = function(arg1, arg2)
    {
        return function() { ... do your stuff with arg1 and arg2 ... };
    }(actualArg1Value, actualArg2Value);

希望我在那里得到了语法。它所做的是创建一个名为 withWrappedArguments() 的函数(迂腐的是,它是一个分配给变量的匿名函数),您可以在任何时间任何地点调用该函数,并且始终使用actualArg1Value 和actualArg2Value 以及您想要放入的任何其他内容那里。如果需要,您还可以让它在调用时接受进一步的参数。秘密是最后一个右大括号后面的括号。这些导致外部函数立即执行,并使用传递的值,并生成可以稍后调用的内部函数。然后在生成函数时冻结传递的值。

这实际上是 bind 所做的,但这样一来,被包装的参数就很明确地只是局部变量的闭包,并且不需要更改 this 的行为。

于 2013-09-07T16:42:31.460 回答
25

在 ES6 中,这很容易通过使用rest 参数扩展运算符来完成。

所以我们可以定义一个bindArgs类似的函数bind,除了只绑定参数,而不绑定上下文this(

Function.prototype.bindArgs =
    function (...boundArgs)
    {
        const targetFunction = this;
        return function (...args) { return targetFunction.call(this, ...boundArgs, ...args); };
    };

然后,对于指定的函数foo和对象obj,语句

return foo.call(obj, 1, 2, 3, 4);

相当于

let bar = foo.bindArgs(1, 2);
return bar.call(obj, 3, 4);

其中只有第一个和第二个参数绑定到bar,而使用调用中指定的上下文obj,并且在绑定参数之后附加了额外的参数。返回值只是转发。

于 2013-05-06T14:12:43.140 回答
17

在本机bind方法this中,结果函数中的值会丢失。但是,您可以轻松地重新编码通用 shim 以不使用上下文参数:

Function.prototype.arg = function() {
    if (typeof this !== "function")
        throw new TypeError("Function.prototype.arg needs to be called on a function");
    var slice = Array.prototype.slice,
        args = slice.call(arguments), 
        fn = this, 
        partial = function() {
            return fn.apply(this, args.concat(slice.call(arguments)));
//                          ^^^^
        };
    partial.prototype = Object.create(this.prototype);
    return partial;
};
于 2012-12-13T01:01:27.893 回答
7

另一个小实现只是为了好玩:

function bindWithoutThis(cb) {
    var bindArgs = Array.prototype.slice.call(arguments, 1);

    return function () {
        var internalArgs = Array.prototype.slice.call(arguments, 0);
        var args = Array.prototype.concat(bindArgs, internalArgs);
        return cb.apply(this, args);
    };
}

如何使用:

function onWriteEnd(evt) {}
var myPersonalWriteEnd = bindWithoutThis(onWriteEnd, "some", "data");
于 2014-02-08T11:42:20.607 回答
7
var b = function() {
    return c(1,2,3);
};
于 2013-11-10T22:29:50.067 回答
2

很难准确地说出你最终想要做什么,因为这个例子有点随意,但你可能想看看部分(或柯里化):http: //jsbin.com/ifoqoj/1/edit

Function.prototype.partial = function(){
  var fn = this, args = Array.prototype.slice.call(arguments);
  return function(){
    var arg = 0;
    for ( var i = 0; i < args.length && arg < arguments.length; i++ )
      if ( args[i] === undefined )
        args[i] = arguments[arg++];
    return fn.apply(this, args);
  };
};

var c = function(a, b, c, callback) {
  console.log( a, b, c, callback )
};

var b = c.partial(1, 2, 3, undefined);

b(function(){})

链接到 John Resig 的文章:http ://ejohn.org/blog/partial-functions-in-javascript/

于 2012-12-13T00:37:57.990 回答
2

可能您想在最后绑定this的引用,但您的代码:-

var c = function(a, b, c, callback) {};
var b = c.bind(null, 1, 2, 3); 

已经应用了绑定,例如this和以后你不能改变它。我建议也将参考用作这样的参数:-

var c = function(a, b, c, callback, ref) {  
    var self = this ? this : ref; 
    // Now you can use self just like this in your code 
};
var b = c.bind(null, 1, 2, 3),
    newRef = this, // or ref whatever you want to apply inside function c()
    d = c.bind(callback, newRef);
于 2018-07-25T06:51:36.857 回答
1

使用protagonist!

var geoOpts = {...};

function geoSuccess(user){  // protagonizes for 'user'
  return function Success(pos){
    if(!pos || !pos.coords || !pos.coords.latitude || !pos.coords.longitude){ throw new Error('Geolocation Error: insufficient data.'); }

    var data = {pos.coords: pos.coords, ...};

    // now we have a callback we can turn into an object. implementation can use 'this' inside callback
    if(user){
      user.prototype = data;
      user.prototype.watch = watchUser;
      thus.User = (new user(data));
      console.log('thus.User', thus, thus.User);
    }
  }
}

function geoError(errorCallback){  // protagonizes for 'errorCallback'
  return function(err){
    console.log('@DECLINED', err);
    errorCallback && errorCallback(err);
  }
}

function getUserPos(user, error, opts){
  nav.geo.getPos(geoSuccess(user), geoError(error), opts || geoOpts);
}

基本上,你想要传递参数的函数变成了一个代理,你可以调用它来传递一个变量,它返回你真正想要做的事情的函数。

希望这可以帮助!

于 2014-02-06T22:36:12.950 回答
1

那么对于你给出的例子,这会做

var b= function(callback){
        return obj.c(1,2,3, callback);
};

如果要保证参数的封装:

var b= (function(p1,p2,p3, obj){
        var c=obj.c;
        return function(callback){
                return c.call(obj,p1,p2,p3, callback);
        }
})(1,2,3,obj)

但如果是这样,你应该坚持你的解决方案:

var b = obj.c.bind(obj, 1, 2, 3);

这是更好的方法。

于 2016-06-09T20:48:51.263 回答
1

我正在使用这个功能:

function bindArgs(func, ...boundArgs) {
    return function (...args) {
        return func(...boundArgs, ...args);
    };
}
// use
const deleteGroup = bindArgs(this.props.deleteGroup, "gorupName1");
于 2021-01-11T16:02:06.617 回答
1

一位匿名用户发布了以下附加信息:

基于这篇文章中已经提供的内容——我见过的最优雅的解决方案是对你的论点和上下文进行柯里化:

function Class(a, b, c, d){
    console.log('@Class #this', this, a, b, c, d);
}

function Context(name){
    console.log('@Context', this, name);
    this.name = name;
}

var context1 = new Context('One');
var context2 = new Context('Two');

function curryArguments(fn) {
    var args = Array.prototype.slice.call(arguments, 1);
    return function bindContext() {
      var additional = Array.prototype.slice.call(arguments, 0);
      return fn.apply(this, args.concat(additional));
    };
}

var bindContext = curryArguments(Class, 'A', 'B');

bindContext.apply(context1, ['C', 'D']);
bindContext.apply(context2, ['Y', 'Z']);
于 2015-07-17T22:58:59.597 回答
1

就这么简单?

var b = (cb) => obj.c(1,2,3, cb)
b(function(){}) // insidde object

更通用的解决方案:

function original(a, b, c) { console.log(a, b, c) }
let tied = (...args) => original(1, 2, ...args)

original(1,2,3) // 1 2 3
tied(5,6,7) // 1 2 5

于 2016-11-08T12:06:49.957 回答
1

使用LoDash,您可以使用该_.partial功能。

const f  = function (a, b, c, callback) {}

const pf = _.partial(f, 1, 2, 3)  // f has first 3 arguments bound.

pf(function () {})                // callback.
于 2017-04-11T18:10:19.123 回答
0

为什么不在函数周围使用包装器将其保存为 mythis ?

function mythis() {
  this.name = "mythis";
  mythis = this;
  function c(a, b) {
    this.name = "original";
    alert('a=' + a + ' b =' + b + 'this = ' + this.name + ' mythis = ' + mythis.name);
    return "ok";
  }    
  return {
    c: c
  }
};

var retval = mythis().c(0, 1);
于 2016-03-02T23:27:04.803 回答
-1

jQuery 1.9 正是通过代理功能带来了这个特性。

从 jQuery 1.9 开始,当上下文为 null 或 undefined 时,将使用与调用代理相同的 this 对象调用代理函数。这允许 $.proxy() 用于部分应用函数的参数而不更改上下文。

例子:

$.proxy(this.myFunction, 
        undefined /* leaving the context empty */, 
        [precededArg1, precededArg2]);
于 2018-01-21T19:50:37.133 回答
-6

jQuery用例:

反而:

for(var i = 0;i<3;i++){
    $('<input>').appendTo('body').click(function(i){
        $(this).val(i); // wont work, because 'this' becomes 'i'
    }.bind(i));
}

用这个:

for(var i = 0;i<3;i++){
    $('<input>').appendTo('body').click(function(e){
        var i = this;
        $(e.originalEvent.target).val(i);
    }.bind(i));
}
于 2014-07-11T14:01:36.507 回答