我知道函数声明被提升到其范围的顶部。这允许我们在JavaScript中实际声明之前使用这些函数:
sayHello(); // It works!
function sayHello() {
console.log('Hello');
}
我也理解闭包使函数能够保留对在同一范围内声明的变量的引用:
function outerFxn() {
let num = 0;
function innerFxn() {
console.log(num);
num++;
}
return innerFxn;
}
const logNum = outerFxn();
logNum(); // 0
logNum(); // 1
logNum(); // 2
到目前为止一切顺利。但是这里有些奇怪,我希望有人可以解释到底发生了什么......
function zero(cb) {
return setTimeout(cb, 0);
}
function test1() {
let txt = 'this is a test message';
function log() {
console.log(txt);
}
zero(log);
}
在上面的示例中,log
函数保留对其创建范围的引用,并保留txt
变量。然后,当稍后在setTimeout
中执行时,它会成功记录txt
变量的值。大。然后就是这个......
function zero(cb) {
return setTimeout(cb, 0);
}
function test1() {
function log() {
console.log(txt);
}
let txt = 'this is a test message';
zero(log);
}
我已将log
函数声明移到了作用域的顶部(无论如何它都会被提升,对吧?),然后我宣布{{1它下面的变量。这一切仍然有效,我不知道为什么。当txt
和log
未被提升时,txt
如何保留对let
变量的引用?是否将分析范围分析为整体?我可以在这里逐步清楚地了解JavaScript引擎的工作原理。谢谢你的土地!
答案 0 :(得分:4)
离开test1
功能后,它是范围的一部分。如果它在此时与var
,let
或const
一起使用并不重要。由于已对整个身体进行了评估,因此该变量存在于范围内。
如果您在评估log
声明之前尝试使用let
,则会出现错误。
编辑:从技术上讲,使用let
和const
声明的变量属于范围,但如果您尝试访问它们,则会将它们整合在一起会导致错误。只有在你得到声明它们被初始化并且你可以访问它们之前。所以它们总是在范围内,只有在评估声明之前才可用。
答案 1 :(得分:3)
“封闭范围是否整体分析?” - 是的闭包保留了您(词汇上)离开时的范围。在您的示例中,当您到达txt
中的结束}
时,test1
确实存在,因此它位于范围内,log
访问它时没有任何问题。
注意上面的“词法”:绑定是在运行时之前完成的,只有重要的是你的块结构。所以即使这样也行不通,但它不应该从“动态”的角度出发:
function test1() {
function log() {
console.log(txt);
}
zero(log);
let txt = 'this is a test message';
}
答案 2 :(得分:0)
在场景2 中,你正在做:
let txt = 'this is a test message'
表示txt
将成为test1()
范围的一部分。 log()
。{li>
其父test1()
的范围。 那么在运行时会发生什么?我们会对test1()
进行评估,因此log()
可以访问test1()
的范围。这意味着txt
可以log()
立即使用。
提示:调试它,设置一些断点,看看会发生什么。
修改:您还可以考虑在log()
内,txt
未定义,因此其值应该是未定义的......对吗? console.log(txt)
工作输出this is a test message
的事实是由于上面对范围界定的解释。将变量声明在函数作用域的顶部,并将函数声明在作用域的底部,因为它们将首先进行评估,这总是很好的做法。在这种情况下考虑人为因素,最佳实践也可以意味着:让您/任何人通过阅读它来了解代码的作用。
答案 3 :(得分:0)
这是一个时间/执行顺序的事情。想想它
function test1(){
var context = { };
function log(){
if(context.hasOwnProperty("txt")){
console.log(context.txt);
}else{
throw new Error("there is no value 'txt' declared in this context");
}
}
context.txt = 'this is a test message';
log();
}
您的代码与未提升的变量txt
相同。当时执行log
,将在适当的函数上下文中声明let txt
。即使没有悬挂也可以使用。函数log
不存储对变量本身的引用,而是存储整个周围的执行上下文,并且此上下文存储变量。