总结一下,the difference between var
and let
是他们在一定范围内的生活。
因此,如果我们以this answer为例:
(function() {
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(`i: ${i}`);
}, i * 100);
}
// 5, 5, 5, 5, 5
for (let j = 0; j < 5; j++) {
setTimeout(function() {
console.log(`j: ${j}`);
}, 1000 + j * 100);
}
// 0, 1, 2, 3, 4
}());
i
(用var
声明)生活在整个function
内j
(用let
声明)仅位于for
循环内。对我来说,这意味着javascript,在每次迭代之后,除了声明和分配给变量外,对于let
,它还需要执行一个额外的步骤:清理j
>
但是,如果我没看错the specs,那还有更多:
var
,将执行以下步骤:IterationStatement:用于(Expressionopt; Expressionopt; Expressionopt)语句
- 如果存在第一个表达式,则
- 让exprRef为评估第一个表达式的结果。
- 让exprValue为GetValue(exprRef)。
- ReturnIfAbrupt(exprValue)。
- 返回ForBodyEvaluation(第二个表达式,第三个表达式,语句,«»,labelSet)。
但是在let
的情况下,将执行12个步骤的庞大列表,包括创建新的声明性环境。
IterationStatement:用于(LexicalDeclaration Expressionopt; Expressionopt)语句
- 让oldEnv成为正在运行的执行上下文的LexicalEnvironment。
- 让loopEnv为NewDeclarativeEnvironment(oldEnv)。
- 让isConst是执行>1。LexicalDeclaration的IsConstantDeclaration的结果。
- 让boundNames为LexicalDeclaration的BoundNames。
- 对于boundNames的每个元素dn
- 如果isConst为true,则
- 执行loopEnv.CreateImmutableBinding(dn,true)。
- 其他,
- 执行loopEnv.CreateMutableBinding(dn,false)。
- 声明:上面对CreateMutableBinding的调用将永远不会返回突然的完成。
- 将运行中的执行上下文的LexicalEnvironment设置为loopEnv。
- 让forDcl是评估LexicalDeclaration的结果。
- 如果forDcl是突然完成的,则
- 将运行中的执行上下文的LexicalEnvironment设置为oldEnv。
- 返回完成次数(forDcl)。
- 如果isConst为false,则让perIterationLets为boundNames,否则让perIterationLets为«»。
- 让bodyResult为ForBodyEvaluation(第一个表达式,第二个表达式,语句,perIterationLets,labelSet)。
- 将运行中的执行上下文的LexicalEnvironment设置为oldEnv。
- 返回完成(bodyResult)。
那为什么为什么在运行以下测试(我从the same question I previously referenced进行测试)时,var
的运行速度却比let
慢,而不是我所期望的相反呢?
(function() {
let varTime = performance.now()
for (var i = 0; i < 100000000; i++) {}
varTime = performance.now() - varTime;
console.log('var', varTime)
let letTime = performance.now()
for (let i = 0; i < 100000000; i++) {}
letTime = performance.now() - letTime;
console.log('let', letTime)
}).call({});
TEST 1
var: 147.500ms
let: 138.200ms
TEST 2
var: 141.600ms
let: 127.100ms
TEST 3
var: 147.600ms
let: 122.200ms
答案 0 :(得分:1)
我同意@Derek朕会功夫的观点,如果不深入研究实现,就很难推理。
但是,如果使用var
,您可能会错过一些步骤:
对于var
,实际上执行了 更多 个步骤:
IterationStatement:用于(Expressionopt; Expressionopt; Expressionopt)语句
- 如果存在第一个表达式,则
- 让exprRef为评估第一个表达式的结果。
- 让exprValue为 GetValue (exprRef)。
- ReturnIfAbrupt(V)。
- 如果Type(V)不是Reference,则返回V。
- 让基为GetBase(V)。
- 如果IsUnresolvableReference(V),则引发ReferenceError异常。
- 如果是IsPropertyReference(V),则
- 如果HasPrimitiveBase(V)为true,则
- 声明:在这种情况下,base永远不会为null或未定义。
- 让基础成为ToObject(base)。
- 返回基数。[[Get]](GetReferencedName(V),GetThisValue(V))。
- 其他基础必须是环境记录,
- 返回base.GetBindingValue(GetReferencedName(V),IsStrictReference(V))(请参阅8.1.1)。
- ReturnIfAbrupt(exprValue)。
- 返回ForBodyEvaluation(第二个表达式,第三个表达式,语句,«»,labelSet)。
的其他处理可能来自GetValue。