【问题标题】:How to bind function arguments without binding this?如何在不绑定 this 的情况下绑定函数参数?
【发布时间】:2012-11-30 19:49:17
【问题描述】:

在 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?

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

谢谢!

【问题讨论】:

  • 只重新绑定this会不会不够? var b = c.bind(this, 1,2,3);
  • 为什么bind() 的第一个值是null 会有问题?似乎在 FF 中工作正常。
  • 如果函数已经有这个绑定,使用 null 作为 bind 的第一个参数会搞砸(即将对象方法绑定到全局范围)。
  • 我真的不清楚你想做什么。您不能在 JavaScript 中完全解除 this 绑定。它总是意味着某事。所以将this绑定到封闭函数的this很有意义。
  • 我是 JS 的新手。我编辑了问题以使其更清楚。我想要的可能是不可能的。谢谢。

标签: javascript


【解决方案1】:

我正在使用这个功能:

function bindArgs(func, ...boundArgs) {
    return function (...args) {
        return func(...boundArgs, ...args);
    };
}
// use
const deleteGroup = bindArgs(this.props.deleteGroup, "gorupName1");

【讨论】:

    【解决方案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);
    

    【讨论】:

      【解决方案3】:

      在 ES6 中,使用 rest parametersspread operator 很容易做到这一点。

      所以我们可以定义一个函数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,并在绑定参数之后附加额外的参数。返回值被简单转发。

      【讨论】:

      • 将 es6 带到桌面上,仍然使用 var :(
      • @DanielKobe 谢谢!扩展运算符和剩余参数早在 letconst 之前就已在 Firefox 中实现,在我第一次发布此内容时还没有。现在更新了。
      • 这很棒,适用于回调函数!也可以更改为附加 args 而不是预先添加它们。
      • 值得注意的是,这会在每次调用 bindArgs() 时创建一个闭包,这意味着性能不如使用原生的 bind() 功能。
      【解决方案4】:

      jQuery 1.9 brought exactly that feature with the proxy function.

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

      例子:

      $.proxy(this.myFunction, 
              undefined /* leaving the context empty */, 
              [precededArg1, precededArg2]);
      

      【讨论】:

        【解决方案5】:

        使用 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.
        

        【讨论】:

          【解决方案6】:

          就这么简单?

          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

          【讨论】:

            【解决方案7】:

            就你给出的例子来说,这样就可以了

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

            这是更好的方法。

            【讨论】:

              【解决方案8】:

              为什么不在函数周围使用包装器将其保存为 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);
              

              【讨论】:

                【解决方案9】:

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

                基于本文已经提供的内容——我见过的最优雅的解决方案是Curry你的论点和上下文:

                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']);
                

                【讨论】:

                  【解决方案10】:

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

                  【讨论】:

                    【解决方案11】:
                    var b = function() {
                        return c(1,2,3);
                    };
                    

                    【讨论】:

                    • 在所有这些嘈杂和模棱两可的答案中,这是一个准确而有用的答案。因此+1。也许您可以改进答案的风格,使其在丛林中那片嘈杂嘈杂的其他地方对肉眼更具吸引力。 const curry = (fn, ...curryArgs) =&gt; (...args) =&gt; fn(...curryArgs, args)
                    • 很好的闭包。谢谢你提醒我生活中更简单的事情
                    【解决方案12】:

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

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

                    【讨论】:

                    • 在调用 apply() 时为此传递“null”似乎更正确
                    【解决方案13】:

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

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

                    希望这会有所帮助!

                    【讨论】:

                      【解决方案14】:

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

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

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

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

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

                      【讨论】:

                      • 请注意,这通常称为“柯里化”。
                      • @M3D 大写,这让它看起来很奇怪。
                      • 如果提供如何使用它的示例,这将更有帮助。
                      • en.wikipedia.org/wiki/Currying 链接适用于任何感兴趣的人,尽管 TLDR 版本是“我们想要 f(x)(y)g=f(x); g(y) 而不是 f(x,y)。所以我们会将 f=(x,y)=&gt;x+y 更改为 f=(x)=&gt;(y)=&gt;x+y 。”
                      【解决方案15】:

                      在本机bind method 中,结果函数中的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;
                      };
                      

                      【讨论】:

                      • 不喜欢你必须使用Function 的原型,但它为我节省了很多时间和一些丑陋的解决方法。谢谢
                      • @jackdbernier:你不必这样做,但我觉得像bind 这样的方法更直观。您可以轻松地将其转换为function bindArgs(fn){ …, args = slice.call(arguments, 1); …}
                      • @Qantas94Heavy:当您关心this 成为a 时,您总是必须这样做。当然,您可以改用bind。然而,OP 确实关心 this 值并且想要一个未绑定的部分函数。
                      • @Bergi:我假设不设置this的目的是该函数使用this,但他们当时不想绑定它。此外,OP 的问题//These should both have exact same output. 中的部分与它的其他部分相矛盾。
                      • @KubaWyrostek:是的,它是为了让部分构造函数工作,有点像子类化(虽然我不推荐这样做)。实际的bind 根本不适用于构造函数,绑定的函数没有.prototype。不,Function.prototype !== mymethod.prototype
                      【解决方案16】:

                      有点难以准确说出您最终想要做什么,因为该示例有点随意,但您可能希望查看部分(或柯里化):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/

                      【讨论】:

                      • 这个函数不起作用,你不能多次调用一个部分应用的函数。
                      • 不确定我是否关注。您能否详细说明一下或发布一个 jsbin 示例?
                      • this example。此外,您的 partial 需要使用 undefined 参数调用才能工作 - 这不是人们所期望的,并且在采用任意数量参数的函数上被破坏。
                      • 凯文,感谢您的回答。它接近我想要的,但我认为this 对象仍然搞砸了。看看:jsbin.com/ifoqoj/4/edit
                      • 哦,明白了。该示例使您要查找的内容更加清晰。
                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2016-02-16
                      • 2016-12-22
                      • 2020-05-11
                      相关资源
                      最近更新 更多