[英]JavaScript: Understanding closures and hoisting
我知道函数声明被提升到其范围的顶部。 这允许我们在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
函数声明移到了作用域的顶部(无论如何它都会被提升,对吧?), 然后我在它下面声明了txt
变量。 这一切仍然有效,我不知道为什么。 如何log
保持到基准txt
时可变let
的和const
的不吊上来? 闭合范围是作为一个整体进行分析的吗? 我可以在这里逐步清楚地了解JavaScript引擎的工作原理。 谢谢SO土地!
离开test1
函数后,它是范围的一部分。 如果它在那一点上与var
, let
或const
一起使用并不重要。 由于已对整个身体进行了评估,因此该变量存在于范围内。
如果在评估let
声明之前尝试使用log
,则会出现错误。
编辑 :从技术上讲,使用let
和const
声明的变量在范围内,但它们是单元化的,如果您尝试访问它们会导致错误。 只有在你得到声明它们被初始化并且你可以访问它们之前。 所以它们总是在范围内,只有在评估声明之前才可用。
“封闭范围是否整体分析?” - 是的 闭包保留了您(词汇上)离开时的范围。 在您的示例中,当您在test1
到达closing }
时, txt
确实存在,因此它在范围内,并且log
访问它时没有问题。
注意上面的“词法”:绑定是在运行时之前完成的,只有重要的是你的块结构。 所以即使这样也行不通,但它不应该从“动态”的角度出发:
function test1() {
function log() {
console.log(txt);
}
zero(log);
let txt = 'this is a test message';
}
在场景2中,您正在做:
let txt = 'this is a test message'
,这意味着txt
将成为test1()
范围的一部分。 log()
,它可以访问其父test1()
的范围。 那么在运行时会发生什么? 将评估test1()
,因此log()
将可以访问test1()
的范围。 这意味着txt
可用于log()
立即使用。
提示:调试它,放置一些断点,看看会发生什么。
编辑:你也可以考虑在log()
,没有定义txt
,因此它的值应该是未定义的......对吗? console.log(txt)
工作输出的this is a test message
是由于上面对范围的解释。 将变量声明在函数作用域的顶部,并将函数声明在作用域的底部,因为它们将首先进行评估,这总是很好的做法。 在这种情况下考虑人为因素,最佳实践也可以意味着:让您/任何人通过阅读它来了解代码的作用。
这是一个时间/执行顺序的事情。 想想就好
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
不存储对变量本身的引用,而是存储整个周围的执行上下文,并且此上下文存储变量。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.