看到一篇讲闭包的文章,有如下代码,这是讲块级作用域一个很经典的代码
function t() {
for(var i = 0;i<2;i++) {
var res = i
let block = i
setTimeout(()=> {
console.log(" this is res :" + res)
console.log(" this is block :" + block)
},0)
}
// res可以输出
console.log(res);
// console.log(block);
console.log(i);
}
文章说,for中使用let创建了块级作用域,导致产生不同的执行上下文
在最开始我想,变量res在词法环境中,两次定时器执行的函数(闭包)都共享了这个变量(这里两个Closure指向同一地址),而变量block应该在变量环境中但是怎么总觉得怪怪的呢?
首先我们要明确几点
for(){},这里的大括号并不能创建执行上下文,执行上下文只包含三种“全局执行上下文”,“函数执行上下文”,“eval执行上下文”for(){}会创建临时的文本执行环境- 上面的代码等价于
function t() {
{
var res = 0
let block = 0
setTimeout(()=> {
console.log(" this is res :" + res)
console.log(" this is block :" + block)
},0)
}
{
var res = 1
let block = 1
setTimeout(()=> {
console.log(" this is res :" + res)
console.log(" this is block :" + block)
},0)
}
console.log(res);
// console.log(block);
console.log(i);
}
针对这里在执行时候到底发生了什么?块级作用域和闭包和作用域又是怎么一回事?我们可以打断点进行观察
下图为运行完block的赋值时,我们可以看到左边变量有三个对象,Block表示块级作用域,Local表示当前作用域,Global表示全局作用域。
我们在闭包打断点,这里是其作用域,Closure表示闭包
稍微改写一下代码,可以得到这样的结果
在函数m的
[[scope]]中存在这三个对象,[[scope]]是函数的私有对象,在函数申明初始化时,根据其上下文生成了。这是函数作用域链构建的第一步。
最后我在这个函数打断点,解决了我的一些疑惑
function t3() {
var res = 0
{
let m = 3
let h = 4
let f1 = function() {
console.log(m);
}
function f2() {
console.log(m);
}
f1()
g2()
}
let no = "hh"
let block = 1
setTimeout(()=> {
console.log(" this is res :" + res)
console.log(" this is block :" + block)
})
}
t3()
- 在运行到
{},变量对象(作用域)里就包含了Block,里面的变量在该区域内可以访问了,当大括号结束,这个临时的空间消失 - 两个函数,一个是
let f1一个是直接申明,直接申明的函数相当于var f2,所以f2在外层作用域 - 查看
f1的Block可以看到只含有变量m,没有变量h。说明其也和Closure一样,只存储函数中使用到的变量 - 这里的
let block没有被大括号圈起来,所以在Loacl中,而不在Block中。(待查证问题:这里的let到底有没有创建块级作用域啊??)