如何在不绑定此函数的情况下绑定函数参数?


115

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

我对此仍然很陌生,很抱歉造成混乱。

谢谢!


1
仅重新绑定是不够的this吗?var b = c.bind(this, 1,2,3);
kojiro 2012年

为什么拥有bind()be 的第一个值为什么会有问题null?似乎在FF中工作正常。
Russ 2012年

7
如果函数已经具有此绑定,则将null用作bind的第一个参数将使事情搞砸(即将对象方法绑定到全局范围)。
Imbue 2012年

1
我真的不清楚您要做什么。您不可能this完全不绑定JavaScript。它总是意味着什么。因此,绑定thisthis封闭函数很有意义。
kojiro 2012年

我是JS的新手。我编辑了问题以使其更清楚。可能是我无法实现的。谢谢。
Imbue 2012年

Answers:


32

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

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

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

希望我在那里得到语法。它的作用是创建一个名为withWrappedArguments()的函数(作为pedantic函数,它是分配给该变量的匿名函数),您可以随时随地调用它,并且始终会与actualArg1Value和actualArg2Value以及要放入的其他任何东西一起使用那里。如果需要,还可以在调用时让它接受其他参数。秘诀是最后一个大括号后的括号。这些使外部函数立即通过传递的值执行,并生成内部函数,以后可以调用。然后,在生成函数时冻结传递的值。

这实际上是bind所做的事情,但是通过这种方式,很明显,包装的参数只是对局部变量的闭包,并且无需更改其行为。


16
请注意,这通常称为“固化”。
M3D

@ M3D大写使它看起来很奇怪。
安德鲁(Andrew)

如果提供了如何使用它的示例,这将大有帮助。
安德鲁(Andrew)

en.wikipedia.org/wiki/Currying链接的人谁是有兴趣的,虽然TLDR版本是“相反的f(x,y),我们希望f(x)(y)还是g=f(x); g(y),所以我们会改变f=(x,y)=>x+yf=(x)=>(y)=>x+y”。
M3D

26

在ES6中,可以使用rest参数spread运算符轻松完成此操作

因此,我们可以定义一个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使用了调用中指定的上下文,并且在绑定的参数之后附加了额外的参数。返回值只是转发。


11
将es6引入表格,仍然使用var :(
Daniel Kobe

7
@DanielKobe谢谢!早在let和之前const,Firefox就已经实现了传播运算符和rest参数,到我第一次发布时尚不可用。现在更新了。
GOTO

这很棒,适用于回调函数!也可以更改以附加args而不是在它们前面添加。
DenisM

1
值得注意的是,每次调用bindArgs()都会创建一个闭包,这意味着性能不如使用本机bind()功能。
约书亚·沃尔什

17

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

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

4
不喜欢您必须使用它的原型这一事实,Function但是它为我节省了很多时间和一些丑陋的解决方法。谢谢
jackdbernier 2013年

3
@jackdbernier:您不必这样做,但我发现它像这样的方法更加直观bind。您可以轻松地将其转换为function bindArgs(fn){ …, args = slice.call(arguments, 1); …}
Bergi

@ Qantas94Heavy:那是什么,你总是有,当你关心做thisabind当然,您可以使用它。但是,OP显然关心该this值,而是需要一个未绑定的部分函数。
Bergi 2014年

@Bergi:我以为未设置的目的this是该函数使用this,但是他们当时不想绑定它。另外,OP的问题//These should both have exact same output.与它的其他部分矛盾。
澳洲航空94重型

1
@KubaWyrostek:是的,这是为了使部分构造函数起作用,有点像子类化(不过,我不建议这样做)。实际bind对构造函数根本不起作用,绑定函数没有.prototype。不,Function.prototype !== mymethod.prototype
Bergi 2014年

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

似乎在调用apply()时为此传递“ null”会更正确
-eddiewould

6
var b = function() {
    return c(1,2,3);
};

3
在所有这些嘈杂和模棱两可的答案中,这是一个准确而有用的答案。因此+1。也许您可以完善答案的样式,以使它在茂密的嘈杂丛林中对肉眼更具吸引力。const curry = (fn, ...curryArgs) => (...args) => fn(...curryArgs, args)
Joehannes

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/


该函数不起作用,不能多次调用部分应用的函数。
Bergi 2012年

不确定我是否遵循。您可以详细说明一下还是发布一个jsbin示例?
凯文·恩尼斯

请参阅此示例。同样,您partial需要使用undefined工作参数来调用它-而不是期望的参数,并且会破坏采用任意数量参数的函数。
Bergi 2012年

凯文,谢谢您的回答。它接近我想要的,但是我认为this对象仍然被弄乱了。看看:jsbin.com/ifoqoj/4/edit
Imbue 2012年

哦,哥奇亚。该示例使您要查找的内容更加清楚。
凯文·恩尼斯

2

可能是您最后要绑定对此的引用,但是您的代码:-

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

已经申请实例绑定这个,以后你不能改变它。我将建议使用reference也是这样的参数:-

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

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

基本上,您要传递参数的函数将成为代理,您可以调用该代理以传递变量,并且它将返回您实际想要执行的功能。

希望这可以帮助!


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

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

这是更好的方法。


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


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.

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

-1

jQuery 1.9通过代理功能完全实现了该功能。

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

例:

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

-5

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));
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.