少年乘勇气,百战过乌孙。这篇文章主要讲述#yyds干货盘点# 详解JavaScript中的闭包相关的知识,希望能为你提供帮助。
一、作用域环境
- 在js作用域环境中访问变量是由内向外的,内部作用域可以获得当前作用域下的变量,和当前作用域外层作用域下的变量
- 外层作用域无法访问内部函数的变量
- 不同的函数作用域中不能相互访问彼此间的变量
如果我们想在一个函数内部也有限权访问另一个函数内部的变量,那么就可以使用闭包,闭包的本质就是在一个函数内部去创建另一个函数。
三、闭包的体现
1、将函数作为返回值
function fn()
let a = 1
return function ()
let b = 1
console.log(a:,++a)
console.log(b:,++b)let f = fn()
f()// 22
f()// 32
这段代码的意思是,将fn的返回值(一个匿名函数)赋给f,f()表示调用fn()返回的匿名函数。
为什么第二次调用a和b的值不一样?
利用闭包的特性保存变量
例:在for循环中每隔0.5秒打印出当前循环的次数
错误解法
for(var i = 0;
i <
5;
++i )
setTimeout(()=>
console.log(i)
,500)
你会发现控制台直接输出了5个5
文章图片
因为js代码执行时有一个任务队列,for循环属于微任务,setTimeout属于宏任务,宏任务会等到微任务执行完毕才会进入javascript的调用栈。具体细节推荐阅读这篇文章:一篇文章快速搞懂JavaScript事件循环、任务队列、同步异步和阻塞非阻塞
也就是说等setTimeout执行时for循环已经执行完毕了,此时i=5,所以输出了5个5
正确解法
for(var i = 0;
i <
5;
++i )
(function(i)
setTimeout(()=>
console.log(i)
,i*500)
(i))// for循环里面是一个立即执行函数,最后面的‘(i)’表示往这个立即执行函数里面传递的参数
- 将setTimeout放进立即执行函数里面,利用闭包存储每次循环时变量i的值
- 然后再将每个定时器设置不同的时间,这样就相当于每一次循环开一个定时器,每个定时器之间相差500毫秒,实现每隔500毫秒输出一次
文章图片
es6中的写法
使用let定义变量i
for(let i = 0;
i <
5;
++i )
setTimeout(()=>
console.log(i)
,500)
var定义的i在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变,但是里面的i,指向的都是同一个i,导致运行时输出的是最后一轮的i的值,也就是 5
es6中引入了let,变量i是let声明的,具有块级作用域,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,javaScript 引擎内部会记住上一轮循环的值,在上一轮循环的基础上进行计算然后初始化本轮的变量i
不过,虽然能顺序输出12345,他们也并不是间隔500毫秒,而是一起输出的。因为let虽然能记住每一次的变量,但是setTimeou的执行顺序不会改变
2、将函数作为参数
var num = 1
var fn = function(a)
if(a >
num)
console.log(a)
else(
console.log(num)
)function fn1(fn2)
var num = 10
fn2(5)fn1(fn)
这段代码会输出5,因为fn被作为参数传入到fn1中,当执行fn2(5)时(fn2(5)相当于fn(5)),5作为参数传入fn中,这时
if(a&
gt;
num)
中的num是当前函数作用域下的num,也就是全局作用域下的num = 1
,而不是fn1中的num = 10
所以5> 1,控制台输出5
四、闭包的优缺点
优点
- 形成私有的执行上下文,使内部私有变量不受外界干扰
- 避免命名冲突
- 解决循环绑定引发的索引问题
【#yyds干货盘点# 详解JavaScript中的闭包】变量不会被垃圾回收机制回收、销毁,导致内存泄漏
推荐阅读
- 这8个JS 新功能,你应该去尝试一下
- OpenHarmony 源码解析之DFX子系统-标准系统接口使用说明
- Shell脚本画图形
- #yyds干货盘点#CCNA学习记录4
- Shell脚本练习三
- Nginx 四层代理之动静分离与负载均衡
- 第五课
- one 一只小红帽的linux学习笔记,从入门到放弃,再到入门
- 第四课