【发布时间】:2020-03-16 05:32:59
【问题描述】:
我一直在努力巩固我对 JS 执行上下文的理解,但无法解释为什么下面的代码没有打印出“hello world”。
var foo = "foo";
function test1() {
console.log(foo)
var bar = "hello world";
test2();
}
function test2() {
console.log(bar);
}
test1();
我(诚然非常不可靠)理解test2() 在test1() 内部执行,可以访问test1() 的执行上下文,并且应该能够通过向上移动来解析变量名称bar作用域链进入test1() 的执行上下文,其中定义了bar。相反,我在尝试打印 bar 时收到参考错误。
由于 JS 的词法作用域,我可以理解下面的代码是如何工作的,但我希望能解释一下 JS 在执行上下文和作用域链方面如何以不同的方式解释它们。
function test1() {
console.log(foo)
var bar = "hello world";
function test2() {
console.log(bar);
}
test2();
}
test1();
到目前为止,我自己寻找解释的最佳尝试是修改第一个代码块,如下所示:
var foo = "foo";
var bar = "not hello world :("
function test1() {
console.log(foo)
var bar = "hello world";
test2();
}
function test2() {
console.log(bar);
}
test1();
在这里,对test2() 的调用会打印出在全局范围内定义的“not hello world :(”。我的第一个想法是test2() 正在沿着范围链上升到test1 的执行上下文,全局执行上下文,但这似乎不对,因为它会在到达全局定义之前在test1()内部找到bar的定义.因此,我的第二个猜测是test2()的定义正在创建在定义全局执行上下文时进行类似“闭包”的捕获,并在 test1() 内调用它会为 bar 生成 null/undefined 的值,因为这是它在函数定义时的值(根本没有声明被提升)。因此,它不会向上移动到 test1() 的执行上下文中来搜索标识符。
任何解释/资源都会非常有帮助。我显然对这门语言很陌生,所以如果我的词汇/术语不太正确,请道歉。提前感谢您的帮助。
【问题讨论】:
-
test2() 在 test1() 内部执行,可以访问 test1() 的执行上下文——这是不正确的。范围由词法确定,意思是“由代码看起来像文本的样子”。
test2()中的语句只能访问函数本身和包含它的函数的范围。 -
感谢您的回复@Pointy。向我解释这个概念的视频是:youtube.com/watch?v=Nt-qa_LlUH0 使用相同的可视化工具 (tylermcginnis.com/javascript-visualizer) 和这段代码,Tyler 的可视化工具暗示 test2() 的执行上下文确实存在于 test1() 的内部。但我确定可视化器可能不正确?从词汇上讲,你能澄清一下“包含它”的确切含义吗?
-
作者称之为“执行上下文”的动态概念和词法作用域的静态概念是有区别的。词法范围可以通过查看代码来确定,而无需运行它。你不知道代码运行时作用域内的变量会有什么值,但你可以通过从内向外工作来确定哪些变量在作用域内,哪些不在作用域内。
-
如何根据您的定义确定函数参数(如回调等)内部声明的匿名函数的词法范围?
-
同理。这都是关于从全局级别到函数体的
{ }嵌套。从函数向外工作以跟踪范围时,您可以通过嵌套的{ }层“出去”,但不能“进入”。您通过向外跟踪遇到的{ }块中声明的任何变量或函数都在范围内; inside 其他{ }块的函数中的变量被隐藏。
标签: javascript closures executioncontext