闭包 浅谈

什么是闭包


MDN给出的定义是可以从内部函数访问外部函数的作用域的一种状态

闭包并不是稀奇玩意, 在JavaScript中闭包无处不在, 并且闭包并不是一个语法, 而是基于词法-----根据源代码中声明变量的位置来确定该变量在何处可用-----作用域写代码时所产生的自然而然的结果, 因此在实际书写中也许没有刻意使用闭包, 但大概率会产生闭包, 比如:

functioninit() {

let name = "sifou"; // name 是一个被 init 创建的局部变量functiondisplayName() { // displayName() 是内部函数,一个闭包alert(name); // 使用了父函数中声明的变量

}

displayName();

}

init(); // sifou

根据前面的定义, 看起来并不像是, 因为displayName函数时嵌套在init内的, 根据作用域链的查找规则, 可以使用上层作用域中的变量是正常的, 而这条查找规则就是产生闭包的最重要的原因. 修改一下上面代码:

functioninit() {

let name = "sifou"; // name 是一个被 init 创建的局部变量functiondisplayName() { // displayName() 是内部函数,一个闭包alert(name); // 使用了父函数中声明的变量

}

return displayName

}

const outFn = init();

outFn() // sifou

由于函数可以作为值进行传递, 因此outFn与displayName实际上是通过不同的标识符调用了内部函数displayName, 而且是在自己定义时的词法作用域外面执行的. 按理来说, 当init执行结束后应该垃圾回收机制回收, 其整个内部作用域都应该被销毁. 而闭包阻止了这件事发生因为该作用域依然被使用, 被displayName使用. displayName依然持有该作用域的引用整个引用就是闭包. 理所当然的, 也就意味着无论以何种方式对函数类型的值进行传递, 当该函数在别处进行调用的时候就可以看到闭包. 本质上一般来说如果将函数作为值类型传递就会有闭包. 比如定时器, 事件监听器, Ajax请求等任务中, 只要使用回调函数实际上就是闭包, 正应了开头所说的在JavaScript中闭包无处不在

一个经典的闭包面试题:

// 改造使其打印1,2,3,4,5for (var i = 1; i <= 5; i++) {

setTimeout(functiontimer() {

console.log(i)

}, i * 1000)

}

// 改造后for (var i = 1; i <= 5; i++) {

(function (j) {

setTimeout(functiontimer() {

console.log(j)

}, j * 1000)

})(i)

}

改造后可以打印1,2,3,4,5的原因与使用let有相似的原因----块级作用域, 在迭代内部使用IIFE会为每一个迭代都生成一个新的作用域, 使延迟函数的回调可以将新的作用域封闭在每个迭代的内部, 并且由于闭包的存在(timer对形参j的使用), IIFE执行后不会被回收, 所以每次的迭代中都有一个正确的变量值进行访问

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值