考虑以下问题:我想创建一个函数数组,每个函数只在该数组中打印其索引。在Python中,可以使用
轻松完成funcs = []
for i in range(5):
funcs.append(lambda i=i: print(i))
funcs[2]()
# 2
这里我们使用默认参数值作为currying的一种方式(如果我理解正确的术语)。
在ES6之前,Javascript中没有默认参数值,因此必须以不同的方式进行currying。现在我们有了它们,我试图将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并尝试理解它的底层。)
答案 0 :(得分:2)
你的问题与 默认参数在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]();
&#13;
或者,如果您想遵循在循环中不定义函数的良好实践,您可以在外部定义函数,并使用.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]();
&#13;
答案 1 :(得分:1)
如果您使用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
所以解析器感到困惑。
答案 2 :(得分:1)
Javascript和Python在闭包中使用后期绑定。但是,使用默认参数是一种破解,让你在Python中模拟早期绑定,这是有效的,因为默认参数在Python中的函数定义时间进行评估。但是,在Javascript默认参数中,根据文档
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters
默认参数在呼叫时被评估,因此不同于在 Python,每次调用函数时都会创建一个新对象。
这是Python中使用的解决方案之一,可以在Javascript中应用于此问题。我尽我所能地将音译成了Javascript。基本上,定义另一个匿名函数,它返回原始函数,并同时应用它。这对我来说非常混乱,在Python中,我总是使用default-argument:
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
,正如其他答案所阐明的那样,而不是这个笨重的解决方案。