说说循环与闭包——《你不知道的JS》读书笔记(一)

什么是闭包

《你不知道的JS》里有对闭包的定义:“当函数可以记住访问所在的词法作用域,即使函数是在当前作用域之外执行,这就产生了闭包。”

讲闭包是啥的太多了...就一句带过...在我的认知中,就是函数套函数,内部的函数能直接访问外部函数的变量。而外部函数的变量,就能被隐藏起来。从而它的优点是能把这些变量隐藏起来,但它的缺点也是这些隐藏的变量难以释放...

总而言之,就是函数里内嵌函数,当这个函数访问外函数的变量时,就可以叫闭包,而真正实现功能呢,就是在外函数return 出这个内部函数,这样外函数的变量就能达到一个隐藏的效果。

(function fn1(){
  var i = 2 ;//变量i就可以被隐藏起来
  function fn2 (){
    console.log(i) ;//内函数访问了fn1的变量
  }
  return fn2;
})()

《循环与闭包》

emmmm最开始本来想写闭包的,然后发现闭包没啥好说的......但是在这期间研究《循环与闭包》这一章有所收获。

for (var i = 1 ; i <= 5 ; ++i) {
  setTimeout( function timer(){
    console.log(i);
  } , i*1000);
}//结果是1s后输出一个6,每隔1s输出1个6,共输出5个6

一个很经典的例子...

1.如果没看过这个小例子的人肯定会很疑惑,为什么结果是这个?

​ 首先,我们先讲讲啥是异步,啥是同步.

在我的理解呢,异步就是同时做很多事情,而同步,就是一次只做一件事情

​ 接着我用程序的视角把这个程序运行一遍

​ 首先进入循环,程序接到任务1,setTimeout,我要过i*1000(此时i=1)ms执行timer(由于setTimeout是异步的,它不会等1s执行完timer再继续循环,它接受到了任务1,就说“好了,我记住了,1s后我会执行timer的”,然后一边等1s,一边进入了循环)。接着,它很快就收到了第2个任务——等2s执行timer,以此类推,最后它接到了5个任务,虽然我们这么看是有先后的,但时间太快,所以几乎是瞬间同时完成的...

​ 接着,1s后,第一个任务完成,叮,执行回调函数timer,这个时候又有很一个关键的,因为timer中的i最后检索到的是全局作用域的i,而这时i已经经历完了循环,此时的i=6。同理,直到5s后,第5个任务完成,输出最后一个6。程序演示就到这里结束了。

所以,知道了它的运行过程,我们知道这个程序与预期实现功能——5s每秒依次输出1,2,3,4,5不同的原因,即最后的timer中的i指的是全局的i,我们只要让它指我们所需要的i(1,2,3,4,5)就能实现,我们也很容易找到解决方案。

  • 立即执行函数
for (var i = 1 ; i <= 5 ; ++i) {
	(function(i){
      	setTimeout( function timer(){
    		console.log(i);
 		} , i*1000);
	})(i);
}
//这里将i立即传进去,形成了封闭的5个函数,timer只能访问到传进去的那个i,也就是我们所需的i。
  • let制造块作用域
for (let i = 1 ; i <= 5 ; ++i) {
	//相当于多了一句 var j = i;后面的i也替换为j
    setTimeout( function timer(){
    	console.log(i);
 	} , i*1000);
}
//这相当于定义了5个不同的i,所以timer找的都是这次运行所对应的i。也就能找到所需的i.

更详细的讲解和拓展可以参考:https://zhuanlan.zhihu.com/p/25407758

Mark一下 https://juejin.im/post/58cf180b0ce4630057d6727c

2.作用域的相关问题

  • 首先,这个程序的作用域结构大致是这样,setTimeout和timer并没有形成闭包,timer也不能访问setTimeout的局部变量。(这是我自己的错,我一直以为这个setTimeout和一个回调函数就可以看作一个闭包的结构....但是通过各种例子证明,回调函数是无法访问setTimeout的局部变量的...)
  • 闭包对于这种循环是一种解决方法。即立即执行函数
for (var i = 1 ; i <= 5 ; ++i) {
	(function(i){
      	setTimeout( function timer(){
    		console.log(i);
 		} , i*1000);
	})(i);
}

通过闭包,作用域的结构大致是这样。所以timer可以访问匿名函数的变量,即参数i。

  • 当然,还有一点,就是当在某一块作用域RHS寻找某一变量找不到时,它会从定义的地方,向上查找,直到找到为止。就比如说这里的timer,他要找i,它会先找自己内部->找匿名函数->全局。

参考:https://segmentfault.com/q/1010000004486903/a-1020000004486973


emmm....第一次写技术文章,写了自己的一些理解,然后可能这些理解有错,大家可以去知乎或者我mark的一些链接去看...写的不好请见谅😧

posted @ 2018-01-05 13:59  Kiznaiver  阅读(323)  评论(1编辑  收藏  举报