图片授权基于 www.pixabay.com 相关协议
老生常谈的话题。
闭包是对它所在作用域的引用,当函数「可以记住并且可以访问」所在词法作用域的时候,就会产生闭包。
先举个例子,最常见面试题:输出 1-10,每隔一秒输出一个自增值。
for (var i = 1; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, i * 1000);
}
?
我以前就是这样写,但发现会输出10个10,并没有按我理解的来输出。
为什么没有按「字面理解」执行?
这是因为 for 循环执行完 1-10 的循环,setTimeout 还没有执行一次。
for 循环执行的是微任务,setTimeout 执行的是宏任务。微任务先执行完毕后,才去执行宏任务。查看关于宏任务文章 JavaScript 执行任务
它们都被封闭在一个作用域中了,因此呢,执行的时候只有一个 i ,共享一个 i 的引用。
如果要实现每隔一秒输出一个自增值,可以使用「闭包」。
修正版:
for (var i = 1; i <= 5; i++) {
(function(j) {
setTimeout(function timer() {
console.log(j);
}, j * 1000);
})(i);
}
使用 IIFE 自执行函数可以为每一次执行都生成一个新的作用域,将每个新的作用域都封闭在作用域内部,每个作用域内部都会有一个正常的值。
要想理解闭包,需要了解「作用域」的问题。闭包就是把当前作用域的访问权限交出去,你在什么地方调用都可以访问「闭包」作用域的内容。关于作用域 JavaScript 词法作用域
function webFn() {
var web = \\\"webtext\\\";
console.log(web); //webtext 可以访问变量 web
}
console.log(web); //web is not defined
?
函数里面的 web 参数可以直接访问到 web 变量。如果函数内部找不到,会继续向外层查找,直到找到最近的一个为止,如果没有找到,则为「is not defined」没有被定义。
外部的 web 不能访问 webFn 函数里面的内容,web 变量被定义在 webFn 函数里。外部不可以访问。
外部如果想访问函数里面的内容,就需要用到「闭包」。
function webFn() {
var web = \\\"webtext\\\";
return web; //返回 web (外部可以访问)
}
var webcopy = webFn();// LHS 获取 webFn() 的value ,webcopy 是 webFn() 的引用
console.log(webcopy); //webtext
看一个《你不知道的JavaScript》书里的例子
function foo(){
var a = 2;
function bar(){
console.log(a);// 2
}
return bar;
}
var baz = foo();
baz();// 2
return bar; 返回的是 bar() 这个函数,bar() 函数呢,是可以访问 foo() 函数内变量 a 的;执行 foo() 后,内部的 bar() 函数赋值给变量 baz 调用 baz(),实际上通过不同的「标识符引用」调用了内部的函数 bar();
如果不 return bar,foo() 这个函数执行完后就是要被「垃圾回收机制」回收的,闭包就阻止了垃圾回收。所以 bar() 可访问的作用域的区域,就叫闭包。
闭包也可以称为「存活作用域」。
无论如何处理,将内部函数的 return 到当前词法作用域之外,它都保有对原始作用域的「引用」,无论在哪执行,执行体都可称为闭包。
参考资料:
[1]《You don\\’t know JavaScript》
[2]https://segmentfault.com/a/1190000011198232
[3]https://www.liaoxuefeng.com/wiki/1022910821149312/1023021187855808
原创文章,作者:小道研究,如若转载,请注明出处:https://www.sudun.com/ask/34551.html