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

IT技术 javascript
2021-02-28 03:11:33

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

我怎样才能避免必须绑定函数的作用域(例如 setting 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?

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

谢谢!

6个回答

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

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

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

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

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

请注意,这通常称为“咖喱”。
2021-04-21 03:11:33
@M3D 大写使它看起来很奇怪。
2021-04-22 03:11:33
en.wikipedia.org/wiki/Currying任何有兴趣的人的链接,尽管 TLDR 版本是“而不是f(x,y)我们想要f(x)(y)g=f(x); g(y)。所以我们会f=(x,y)=>x+y改为f=(x)=>(y)=>x+y。”
2021-04-29 03:11:33
如果提供了如何使用它的示例,这将更有帮助。
2021-05-02 03:11:33

在 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使用调用指定的上下文,并在绑定参数之后附加额外的参数。返回值只是简单地转发。

这很棒,适用于回调函数!也可以更改为附加 args 而不是预先添加它们。
2021-04-23 03:11:33
将 es6 带到表中,仍然使用 var :(
2021-05-04 03:11:33
@DanielKobe 谢谢!很早以前在 FirefoxletFirefox 中就已实现了扩展运算符和其余参数,在const我第一次发布此内容时,它们还不可用。现在已经更新了。
2021-05-04 03:11:33
值得注意的是,每次调用 bindArgs() 时都会创建一个闭包,这意味着性能不如使用本机 bind() 功能。
2021-05-16 03:11:33

在天然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;
};
不喜欢你必须玩原型的事实,Function但它为我节省了很多时间和一些丑陋的解决方法。谢谢
2021-04-22 03:11:33
@Bergi:我假设不设置的目的this是函数使用this,但当时他们不想绑定它。此外,OP 问题中的//These should both have exact same output.一点与它的其他部分相矛盾。
2021-04-23 03:11:33
@ Qantas94Heavy:那是什么,你总是有,当你关心做thisabind当然,您可以改用然而,OP 并不明确关心这个值,而是this想要一个未绑定的部分函数。
2021-05-09 03:11:33
@jackdbernier:您不必这样做,但我发现它作为像bind. 您可以轻松地将其转换为function bindArgs(fn){ …, args = slice.call(arguments, 1); …}
2021-05-16 03:11:33
@KubaWyrostek:是的,它是为了使部分构造函数起作用,有点像子类化(虽然不是我推荐的)。实际bind根本不适用于构造函数,绑定函数没有.prototype. 不,Function.prototype !== mymethod.prototype
2021-05-20 03:11:33

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

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”会更正确
2021-05-14 03:11:33
var b = function() {
    return c(1,2,3);
};
很好的闭包。谢谢你让我想起生活中更简单的事情
2021-05-06 03:11:33
在所有这些嘈杂和模棱两可的答案中,这是一个准确而有用的答案。因此+1。也许您可以改进答案的风格,使其在丛林中那些花哨的嘈杂休息区对肉眼更具吸引力。const curry = (fn, ...curryArgs) => (...args) => fn(...curryArgs, args)
2021-05-15 03:11:33