#yyds干货盘点# 详解JavaScript中的闭包
Posted 啊a阿花
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了#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() // 2 2
f() // 3 2
这段代码的意思是,将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>num)
中的num是当前函数作用域下的num,也就是全局作用域下的num = 1
,而不是fn1中的num = 10
所以 5>1,控制台输出5
四、闭包的优缺点
优点
- 形成私有的执行上下文,使内部私有变量不受外界干扰
- 避免命名冲突
- 解决循环绑定引发的索引问题
缺点
变量不会被垃圾回收机制回收、销毁,导致内存泄漏
以上是关于#yyds干货盘点# 详解JavaScript中的闭包的主要内容,如果未能解决你的问题,请参考以下文章
[Flutter]专题Flutter 中的 AppBar详解#yyds干货盘点#
# yyds干货盘点 # 盘点JavaScript中的事件及事件的三种模型