从setTimeout来看javascript的作用域

首先我们来看这样一段代码:

 for (var i=0; i<3; i++) {
     setTimeout(() => {
         console.log(i)
     }, 0)
 }

很多刷过面试题的jser看到这段代码应该很熟悉了,脱口就能说出正确答案是 3 个 3,但有不少人却不明白其中的原理。别小看这段代码,其中暗含了不少的js的基础知识。
从代码层面来,很多童鞋会想当然的将上述代码理解成以下的内容:

    for (var i=0; i<3; i++) {
        console.log(i)
    }

即,输出为0、1、2。

如果这样想的话,他们至少犯了2个错误:

  1. 写出第一段代码的童鞋,都是试图假设循环中的每个迭代在运行时都会给自己“捕获”一个 i 的副本。但是根据作用域的工作原理,实际情况是尽管循环中的五个函数是在各个迭代中分别定义的,但是它们都被封闭在一个共享的全局作用域中,因此实际上只有一个 i 。
  2. setTimeout等延迟函数的回调会在循环结束时才会执行,故而不管循环多少次,延迟多少时间,console.log中的i,必然是共享的全局作用域中变量i,在此例中就是3。

从第一段代码的执行顺序来看,其演示代码应该是:

var i=0;
i++;
i++;
i++;
console.log(i);
console.log(i);
console.log(i);

上述代码就很好地解释了第一段代码3个3的执行结果了。

~~ 那怎样才能使上述代码的输出为0、1、2呢?

提供两种方法:

  1. 使用let
    for (let i=0; i<3; i++) {
        setTimeout(() => {
            console.log(i)
        }, 0)
    }

let 本质上这是将一段代码块转换成一个可以被关闭的作用域。或许这样看会比较直观。

    for (var i=0; i<5; i++) {
        let j = i
        setTimeout(() => {
            console.log(j)
        }, 0)
    }

let的存在,会产生一个隐形的作用域,使得每次循环的 i 值通过复制给隐形作用域内的 j 保存下来。

  1. 使用function创建独立作用域
    for (var i=0; i<3; i++) {
        (function(j) {
            setTimeout(() => {
                console.log(j)
            }, 0)
        })(i)
    }

众所周知,js 中 function 可以提供一段独立的作用域,那我们就可以通过在迭代内使用function会为每个迭代都生成一个新的作用域,使得延迟函数的回调可以将新的作用域封闭在每个迭代内部,每个迭代中都会含有一个具有正确值的变量供我们访问。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值