当定义一个c++ lambda https://en.cppreference.com/w/cpp/language/lambda时,有一个捕获块捕获封闭作用域中的变量的 * 值 *(至少如果变量是通过复制而不是通过引用捕获的)。因此,如果lambda使用了一个已经捕获的变量,并且该lambda稍后被执行,则lambda内的相应变量将具有当lambda被定义时它所具有的值。
使用javascript的arrow函数,我可以从封闭的作用域中引用变量,但是,当arrow函数被调用时,它将使用它现在拥有的变量值(而不是定义arrow函数时拥有的值)。
是否有类似的变量捕获机制,允许使用箭头函数对象存储捕获的变量值?
下面是一个c++的例子:
// Example program
#include <iostream>
#include <string>
#include <functional>
int main()
{
std::function<void(int)> byCopyCaptures[5];
std::function<void(int)> byRefCaptures[5];
for(int i=0; i<5; i++) {
// The variable i is captured by copy:
byCopyCaptures[i] = [i](int j) {std::cout << "capture by copy: i is " << i << " and j is "<< j <<"\n";};
// The variable i is captured by reference:
byRefCaptures[i] = [&i](int j) {std::cout << "capture by ref: i is " << i << " and j is "<< j <<"\n";};
}
for(int k=0; k<5; k++) {
byCopyCaptures[k](k);
byRefCaptures[k](k);
}
}
输出:
capture by copy: i is 0 and j is 0
capture by ref: i is 5 and j is 0
capture by copy: i is 1 and j is 1
capture by ref: i is 5 and j is 1
capture by copy: i is 2 and j is 2
capture by ref: i is 5 and j is 2
capture by copy: i is 3 and j is 3
capture by ref: i is 5 and j is 3
capture by copy: i is 4 and j is 4
capture by ref: i is 5 and j is 4
使用箭头函数的javascript等价物是什么?
4条答案
按热度按时间w7t8yxp51#
我认为与之最接近的等价方法是使用immediately invoked function expression,并传入要锁定的值。由于代码现在访问的是函数参数而不是原始变量,因此对原始变量的赋值无关紧要。例如:
因为我对所有的变量都使用了相同的名称,所以可能会有点混淆什么是什么,所以这里有一个唯一变量名的相同问题:
afdcj2ne2#
我的javascript不是很好。
但你所寻找的概念是一个结束:
回到你的例子:
fzsnzjdm3#
您描述的行为涉及几个相关的JavaScript概念:作用域、执行上下文(提升)和闭包。
我没有看到JavaScript代码示例,所以我对你的代码结构做了一些假设。我的示例是用TypeScript编写的,但在JavaScript中看起来并没有太大的不同。
此代码具有以下输出:
尽管有这样的行为,但说这是通过引用传递并不完全准确,实际情况是作用域在JavaScript中的工作方式与在C++中的不同。
JavaScript在2015年之前没有任何区块范围,当时JavaScript的规范ECMAScript 6(ES6)发布。
看这个例子:
这段代码是有效的JavaScript并打印
42
。其他语言的开发人员可能会认为它有错误或打印未定义。为了向后兼容,即使在现代JavaScript中,这种行为也是相同的。JavaScript确实有函数作用域,结果如下:
当输入一个函数时,JavaScript解释器会像其他语言一样将执行上下文推送到堆栈上。从堆栈开始,它基本上会执行两次传递:1)它为所有的函数和变量创建绑定,2)它执行代码。因为解释器在执行代码时知道一切,所以函数和变量看起来"提升"到作用域的顶部。
这种提升允许你在函数定义之前调用它,你也可以提前调用变量,但这几乎总是一个坏主意。
回到这个例子,正如你所观察到的,lambda函数中的变量直到运行它才被求值,在此之前,函数创建一个闭包来决定它可以访问哪些函数和变量,闭包包括函数本身和它的词法环境。
在本例中,虽然这10个函数都是唯一的,但它们都共享相同的词法环境,当对它们求值时,它们都指向相同的
i
和k
值,解释器之前将这些值提升到函数的顶部。这里的
i
和k
是原语,在JavaScript中总是通过值传递,所以尽管行为类似于通过引用传递,但这里不是这样。有几种方法可以在lambda创建时捕获值,可以使用立即调用函数表达式(IFFE):
IFFE和其他函数一样,只不过是在声明的时候执行的。这样写会更清楚:
i
通过值直接传入,然后lambda有一个副本。你也可以 Package 一个更大的代码段来创建一个新的作用域,JavaScript库曾经必须这样做,因为所有的东西都是全局作用域。
如果不喜欢IFFE语法,可以像调用其他函数一样调用它。
另一个选项是创建一个helper函数,这是一种更简洁的方法,这个选项的作用和上面的一样,
i
直接传值。最好的方法,也是除了极少数情况之外普遍推荐的方法,是永远不要再使用
var
,而是使用const
和let
,这是ES6中规范添加的声明变量的新方法。关于最佳实践的共识通常建议尽可能多地使用
const
,并且仅在需要修改变量时使用let
。当使用这些函数时,它们实际上是块作用域的,并且会像您期望的那样从C++中执行。
我希望这能有所帮助。JavaScript中有很多怪癖,尽管它比以前好得多。我建议使用像ESLint这样的静态分析工具。它会指出像这样的常见问题和陷阱。
TypeScript也要好得多,是我现在对任何新项目的推荐。它会在编写代码时指出更多的问题,而不是在运行时弄清楚。
9rygscc14#
这是我今天学习Go语言时发现的一个“按值捕获”方法。使用
const iCopy = i
强制立即复制值,然后闭包捕获iCopy的常量值,而不是对i
的引用。产出