找回密码
 注册账号

QQ登录

只需一步,快速开始

《泰拉瑞亚下载-1.4.2.3版》 泰拉瑞亚服务器 - MOD模组下载

入驻泰拉战网 新手指引 - 升级 - 师徒

泰拉瑞亚合成表 泰拉卡牌 - 泰拉江湖 - 泰拉刺客

联系泰拉开发组👈进入 积分市场 - 房产交易 - 水晶获取

查看: 1723|回复: 0

浅谈 循环中的setTimeout

[复制链接]
小丑
法师

309

主题

21

回帖

18

广播

见习版主

积分
774
泰拉
11443
水晶
5
铜钥匙
3
银钥匙
0
金钥匙
0

【江湖新秀】【我是小土豪】【宝剑回鞘】【泰拉达人】【奥运选手】【潜水高手】

发表于 2022-1-27 15:55:27 | 显示全部楼层 |阅读模式
技术交流
文章类型: 逻辑思维
涉及领域: Javascript 
内容难度: 噩梦

先来看看一个js中讲作用域最经典的代码片段

for(var i=1;i<=3;i++){undefined

setTimeout(function(){undefined

console.log(i);

},i*1000)

},

每个初学者第一反应一定以及肯定是输出1,2,3,即1s->1,2s->2,3s->3,

but,sorry 请看正确答案



1s->4,2s->4,3s->4

卧槽(你们是不是卧槽了一下),输出的是循环结束的最终值。

/******(引用《你不知道的javascript》)********/

仔细想一想,延迟函数的回调会在循环结束时才执行,

当代码改成

for(var i=1;i<=3;i++){undefined

setTimeout(function(){undefined

console.log(i);

},0)

},

输出的还是3个4,所以可以看出循环中的settimeout函数都是在循环结束后才执行的。

这里引申出一个更深入的问题,代码中到底有什么缺陷导致它的行为同语义不一致呢?

我们在之前一直以为每次循环都会绑定唯一值i,所以每次输出i的值肯定不一样,但是其实他们都共享了一个i(一个共享的全局作用域)。

这时候你会想:但是我们要的是绑定唯一值啊,咋就共享了呢,可不可以分为3个独立的作用域?

这时候我来帮你想想把******************

IIFE听过没,他全名叫做立即执行函数,IIFE会通过声明并立即执行一个函数来创建作用域。

你:说这么多干啥,试试看呀!

我们来试一下:(让你看的舒服点我截图给你看)



结果:



你:舒服有啥用哦,还不是原来的错误,没用啊你这个方法。。。。

额,等等再骂我,给我点时间解释,我们现在看起来有很多词法作用域了,的确每个延迟函数都会将IIFE再每次迭代中创建的作用域封装起来。

但是这样在setTimeout中的变量i还是外部的共享变量i,所以我们要在立即执行函数内部存在每次循环的i,

看图:





结果:



你:卧槽(占时不骂你了),好像成功了!,但是这好像好不习惯,IIFE我都不怎么用的啊,就没有别的办法了吗?

有的,不卖关子了,直接上代码:



结果:



let是es6新引入的关键字,它的作用是将变量绑定到作用域中,所以说当你不能确定是否存在共享作用域时的情况时(并且你也不希望共享时),可以直接将var替换成let。

  你:直接把for循环里的var换成let不行吗?

你说的对,这样是最简单明了的,



结果:



另外当然还有方法啦,引用IT前出塞的文章:https://baijiahao.baidu.com/s?id ... r=spider&for=pc

(3)利用ES 5引入的bind函数

for (var i=1; i<=3; i++) {undefined

setTimeout( function timer(i) {undefined

console.log(i);

}.bind(null,i), i*1000 );

}

注:{}.bind(null,i)表示将i传给{}作用域中,null是因为我们并不关心硬绑定的this是什么

(4)利用setTimeout第三个参数

for (var i=1; i<=3; i++) {undefined

setTimeout( function timer(i) {undefined

console.log(i);

}, i*1000,i );

}

注:第三个参数的意义就是将i传给函数,(setTimeout函数第三个参数及以后的参数都可以作为timer函数的参数)。

(5)把setTimeout用一个方法单独出来形成闭包

var loop = function (i) {undefined

setTimeout(function timer() {undefined

console.log(i);

}, i*1000);

};

for (var i = 1;i <= 3; i++) {undefined

loop(i);

}

注:闭包就是在本身词法作用域以外的地方去调用它时,它可以记住并访问所在的词法作用域  。

总结:记住一点,for()循环是虚假的块作用域(实际上就不是块作用域)所以内部定义的变量i实质上是全局变量所以被共享了。

     if()也不是块作用域。常见语言java和c里面for和if中定义的变量都是局部变量,但在js中是全局变量
[发帖际遇]: 赵匡胤 乐于助人,奖励 4 泰拉. 幸运榜 / 衰神榜
您需要登录后才可以回帖 登录 | 注册账号

本版积分规则

QQ|友链申请|Archiver|手机版|小黑屋|游芯沙盒 ( 陕ICP备11006283号-1 )

GMT+8, 2024-5-5 17:00 , Processed in 0.108720 second(s), 37 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表