这是我去测试一种语言是否具有动态名称解析。
function foo() {
function bar() {
print a
}
var a = 10
bar()
}
如果语言使用动态名称解析,则代码应打印10.否则,应抛出未定义的错误。
Javascript打印10.但是Javascript使用变量提升,将var a
移动到顶部foo并使我的测试无效。
编辑: 如果我们可以删除JS中的变量,那么以下将是一个很好的测试:
var a = 5
function foo() {
var a = 10
function bar() {
print a
}
delete a
bar()
}
foo()
如果JS静态地解析名称,bar的a
引用foo的a
。由于foo的a
被删除(如果可能),bar会打印undefined
。
如果JS动态解析名称,则在调用bar()时将动态查找bar a
。由于foo的a已经被删除,因此查找将找到全局a,并且bar将打印5.
答案 0 :(得分:4)
Javascript是否使用动态名称解析?
是。请考虑以下example:
eval("var foo = 'foo';");
console.log(foo);
// > "foo"
变量foo
直到运行时才被绑定到词法环境(由于eval()
语句),但是没有抛出错误(并且代码有效)的事实证明了名称是动态解决的。
但是Javascript使用变量提升,它将var a移动到顶部foo并使我的测试无效。
注意:也许您只是说吊装妨碍了您尝试执行的测试?如果是这样,请忽略此答案的其余部分......
此行为实际上是通过提升来解释的,而不是由它无效。即,
正如您所指出的,由于提升,变量a
得到已创建(但尚未分配给) foo()
函数。
接下来,您有一个函数声明。碰巧的是,函数声明也会被提升到其作用域的顶部。
接下来,您将值10
分配给a
。请注意,在实际调用bar()
之前会发生这种情况。
最后,您实际调用了bar()
,此时已为a
分配了值10
,导致0
被打印出来。
将所有这些组合在一起,您的foo()
函数的行为与按如下方式编写的行为相同:
function foo() {
// hoisted
var a;
// also hoisted
function bar() {
// due to hoisting, `a` is lexically in scope here
console.log(a);
}
// the actual assignment
a = 10
// the invocation
bar()
}
我恰巧在昨晚的答案中提供了对声明和赋值 / 初始化之间差异的相当详尽的解释。它解释了这里看到的大部分行为:Declaring vs Initializing a variable?