默认参数和柯里化:Python 与 Javascript

Default parameters and currying: Python vs. Javascript

考虑以下问题:我想创建一个函数数组,每个函数只打印其在该数组中的索引。在 Python 中,使用

可以轻松完成
funcs = []
for i in range(5):
   funcs.append(lambda i=i: print(i))
funcs[2]()
# 2

这里我们使用默认参数值作为柯里化的一种方式(如果我理解正确的话)。

在 ES6 之前,Javascript 中没有默认参数值,因此柯里化必须以不同的方式完成。现在我们有了它们,我试着将 Python 直译为 Javascript:

funcs = []
for (var i=0; i<5; i++) {
   funcs.push(function (i=i) {console.log(i)})
}
# this part pass OK
funcs[2]()
ReferenceError: i is not defined
    at Array.<anonymous> (evalmachine.<anonymous>:3:27)
    at evalmachine.<anonymous>:1:9
    at ContextifyScript.Script.runInThisContext (vm.js:26:33)
    at Object.exports.runInThisContext (vm.js:79:17)
    at run ([eval]:608:19)
    at onRunRequest ([eval]:379:22)
    at onMessage ([eval]:347:17)
    at emitTwo (events.js:106:13)
    at process.emit (events.js:191:7)
    at process.nextTick (internal/child_process.js:752:12)

为什么会失败? Python 和 Javascript 传递默认值的方式有什么区别?

(好吧,我知道我可以在这里使用 let 而不是 var,我只是在用 Python 学习 Javascript 几年后尝试理解它的底层。)

您的问题与 when default parameters get bound in closures in python 与 JavaScript。虽然 JavaScript 和 Python 都使用后期绑定,但在默认参数的情况下,Python 模拟早期绑定,而 JavaScript 则不。

话虽如此,如果您要创建这样的闭包,您不妨利用它们并将所有参数一起抛弃,老实说。

您提到了 let 的使用,如果您想在 for 循环内定义函数,这一点很重要,否则 funcs[n] 将始终是迭代器的最大值(由于JavaScript 闭包的后期绑定)。

试试这个:

funcs = [];
for (let i=0; i<5; i++) {
   funcs.push(function () {console.log(i)});
}
funcs[2]();

或者,如果您想遵循不在循环内定义函数的良好做法,您可以在外部定义函数,并使用 .bind() 传递变量。需要注意的一件事是,此方法会将变量与调用 .bind() 时的值绑定,因此您不必使用 let

funcs = [];
function myFunc(i) {
  console.log(i);
}
for (var i=0; i<5; i++) {
   funcs.push(myFunc.bind(this, i));
}
funcs[2]();

如果您使用 let 而不是 var,则每次迭代都会获得一个新的绑定。

funcs = []
for (let i=0; i<5; i++) {
  funcs.push(function (j=i) {console.log(j)})
}

funcs[2];//2

i=i 不起作用,因为在 ES6 中参数可以使用其他参数定义默认值

f = function(a=1, b=a){console.log(b);}
f() // 1

所以解析器变得混乱了。

Javascript 和 Python 都在闭包中使用后期绑定。但是,使用默认参数是一种技巧,让您可以在 Python 中模拟早期绑定,并且之所以有效,是因为默认参数是在 Python 中的 函数定义时间 评估的.但是,根据文档

,在 Javascript 默认参数中

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters

The default argument gets evaluated at call time, so unlike e.g. in Python, a new object is created each time the function is called.

这是Python中使用的解决方案之一,可以应用于Javascript中的这个问题。我已尽我所能音译成 Javascript。本质上,定义 另一个 匿名函数 returns 您的原始函数,并同时应用它。这对我来说非常混乱,在 Python 中,我总是使用默认参数:

funcs = []
for (var i = 0; i < 5; i++) {
    funcs.push((function (i) {return function() {console.log(i)}})(i))
};
funcs[0]() // 0
funcs[4]() // 4

在Python中:

>>> funcs = []
>>> for i in range(5):
...     funcs.append((lambda i: lambda : print(i))())
...
>>> funcs[0]()
0
>>> funcs[4]()
4

我认为很明显您应该在 Javascript 中使用 .bind,正如其他答案中所阐明的那样,而不是这个笨拙的解决方案。