您的位置 首页 > 腾讯云社区

js闭包就那么回事---brzhang

今天了解了一下js闭包这块的内容,还是有点诡异的,将实践结果记录一下,看完只后,我敢说,闭包就那么回事,所谓的闭包,其实就是客户端开发中,其实就是叫做内存泄漏,就是不当引用导致对象没法得到释放,哈哈,玩笑开得有点过了,只是有点像哈,其实并不全是。

for(var i=1;i<=5;i++){ setTimeout(function timer(){ console.log(i) },i*1000) }

你能说出,这个写法将会输出什么吗?先不要看下面的答案,自己凭自己的经验说一下。

据说至少一半的初学者会回答1,2,3,4,6

等等,那个8903是个什么鬼,其实那个8903是setTimeout函数的返回值,本来应该打印5个出来的,如下图所示:

但是为什么只打印了1个出来呢,不是应该打印5个吗?这个问题,也许你没有思考过,然而,我以前也没有思考过,但是今天我通过实验弄清楚了由来。看下面两个实验。

所以,我们知道,我们把代码贴到console控制台上去执行的时候,实际上基本上等价于。

console.log(eval(`let ret = 0 let fun = function () { return ret++ } for (var i = 1; i <= 5; i++) { fun() }`))

而,我们知道,eval返回值的规则,如果你不知道,可以在这里了解eval() - JavaScript | MDN

因为,回到我们的最初代码

for(var i=1;i<=5;i++){ setTimeout(function timer(){ console.log(i) },i*1000) }

这里打印了一个8903就不难理解了。但是,我想说的是,这个说了这么多,只是一个插曲而已,本文的重点不是这个,就上述这段代码,我们的本意是想让它打印1.2.3.4.5的,结果你给我5个6。那么,改为下面这个方式呢?

for (var i = 1; i <= 5; i++) { (function () { setTimeout(function timer() { console.log(i) }, i * 1000) })() }

使用一个IIFE将其包裹起来,那么实际的执行结果将会符合我们的预期吗?下不要看下面的答案,同样,你自己感受一下。

没错,同样的道理,并不符合我们的预期。

继续改,看下面

for (var i = 1; i <= 5; i++) { var j = i; (function () { setTimeout(function timer() { console.log(j) }, j * 1000) })() }

可以看到,效果确实和上一次差不多,但是为什么输出的是5个5呢?那是因为,最后一次i++影响不到j了,所以是5。

继续改

for (var i = 1; i <= 5; i++) { (function () { var j = i setTimeout(function timer() { console.log(j) }, j * 1000) })() }

嗯,我们把var j =i 拿到了IIFE里面了,那么这次执行的结果会符合我们的预期吗?答案是:

符合了!!!,那么为什么,我们分析setTimeout所处的作用域中,IIFE每次执行,相当于甩出了一个闭包,每个j都是独立私有的,不在是外面那个i(等同于全局变量)。因此,执行结果符合我们的预期。那每次这样写是不是有点坑。感觉包了好多,又难以理解,有没有更加easy的办法呢?有 ,结合let。

for (var i = 1; i <= 5; i++) { let j = i setTimeout(function timer() { console.log(j) }, j * 1000) }

仅仅只是换了一个let,就做到了我们的想要的预期结果,那么这是为什么呢?

块级作用域,此时的j在每次的循环中存在,下个循环,j就是另外一个j了,换句话说,下次循环,此j非彼j,上述代码实际还等价于

for (let i = 1; i <= 5; i++) { setTimeout(function timer() { console.log(i) }, i * 1000) }

其结果是一样,这就是所为什么使用let,少使用var,因为var稍稍使用不慎,就会污染全局。

---来自腾讯云社区的---brzhang

关于作者: 瞎采新闻

这里可以显示个人介绍!这里可以显示个人介绍!

热门文章

留言与评论(共有 0 条评论)
   
验证码: