必须包含在返回函数具有其自己独立可访问的外部作用域中。
实施范围和背景
js – 范围字符串
js – 执行上下文
我们从简单的例子开始
function wrapperFunction() {
let name = 1;
return function () {
console.log(name);
}
}
let fuc = wrapperFunction()
fuc()
这里的fuc范围构成了一个必要包的简单环境。
最大的特点是func目前只能访问wrapperFunction的环境变量。你可能会说wrapperFunction可以访问自己,但是当wrapperFunction执行时,这是一个具有权限的不同环境。因为func的环境变量是依赖内存来存储wrapperFunction变量的。
碧宝什么时候消失
变量的寿命取决于盖子的寿命。闭包覆盖的外部作用域中的变量将一直存在,直到闭包函数被销毁。如果多个闭包引用一个变量,则在收集所有闭包之前,该变量不会被销毁。
因此,当不再使用必要的包时,我们将其删除。
juejin.cn/post/684490…
定时器
(函数自动运行(){
令x = 1;
setTimeout(function log(){
console.log(x);
}, 10,000);
})();
复制代码变量 x 将处于活动状态,直到执行计时器回调或调用 clearTimeout() 为止。
如果这里使用setInterval(),变量x将一直起作用,直到调用clearInterval()。
译者注:原文中说变量x一直存活到调用setTimeout()或setInterval()之前是不正确的。
活动
(函数自动运行(){
令x = 1;
$(“#btn”).on(“点击”, function log(){
console.log(x);
});
})();
复制代码 当 x 变量在事件处理程序中使用时,它将一直有效,直到事件处理程序被删除为止。
阿贾克斯
(函数自动运行(){
令x = 1;
加载(“http://”).then(函数log(){
console.log(x);
});
})();
复制代码变量 x 一直存在,直到后端返回结果并触发回调函数。
在前面的示例中,我们可以看到 log() 函数在父函数执行后仍然处于活动状态,并且 log() 函数是闭包。
除了常见的异步任务(例如计时器、事件处理和 Ajax 请求)之外,还有其他异步 API(例如 HTML5 Geolocation、WebSockets 和 requestAnimationFrame())也会使用此闭包函数。
变量的寿命取决于盖子的寿命。闭包覆盖的外部作用域中的变量将一直存在,直到闭包函数被销毁。如果多个闭包引用一个变量,则在收集所有闭包之前,该变量不会被销毁。
经典面试题
编写代码:期望输出 5, 0, 1, 2, 3, 4
每秒返回结果必须包含
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
console.log(i); // 5, 5, 5, 5, 5, 5
定时器的执行机制是,它将当前函数放入事件队列中,直到主进程启动后才执行。所以我们在这里看看结果。
当执行setTimeout 时,第一个回调函数被移至事件队列,与其他 setTimeout 的结果相同。那么因为它们的外部范围都是相同的。两者都可以访问变量 i。因为i的值没有块范围。在循环过程中,这个变量累计到5。
立即执行表达式
for (var i = 0; i < 5; i++) {
(function(j) {
setTimeout(function() {
console.log(j);
}, 1000);
})(i);
}
console.log(i);
的处理方式如下:函数表达式立即执行 IIFE +参数复制为每个setTimeout创建唯一的父作用域。 。通过函数执行模式
api操作超时设置
for (var i = 0; i < 5; i++) {
setTimeout(function(j) {
console.log(j);
}, 1000, i);
}
console.log(i);
这种做法是将参数复制为函数本身的对象变量的方法。
让
块级别的范围处理
var j = 0
for (let i = 0; i < 5; i++, j++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
console.log(j);
承诺
每秒预计输出:0-1-2-3-4-5
const tasks = [];
for (var i = 0; i < 5; i++) {
((j) => {
tasks.push(new Promise((resolve) => {
setTimeout(() => {
console.log(j);
resolve();
}, 1000 * j);
}));
})(i);
}
Promise.all(tasks).then(() => {
setTimeout(() => {
console.log(i);
}, 1000);
});
// let 方式
const tasks = [];
for (let i = 0; i < 5; i++) {
tasks.push(new Promise((resolve) => {
setTimeout(() => {
console.log(i);
resolve(i);
}, 1000 * i);
}));
}
Promise.all(tasks).then((res) => {
setTimeout(() => {
console.log(res[res.length - 1] + 1);
}, 1000);
});
工作中的应用
具有内部状态或内部处理的代码块。
- 高阶特殊函数
- redux
- 路由器
关闭导致内存泄漏
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
</style>
</head>
<body>
<div class="left">LEFT</div>
<div class="right">RIGHT</div>
<script type="text/javascript">
var t = null;
var replaceThing = function () {
var o = t;
var unused = function () {
if (o) {
console.log("hi");
}
}
t = {
longStr: new Array(100000).fill('*'),
someMethod: function () {
console.log(1)
}
}
}
setInterval(replaceThing, 1000)
</script>
</body>
</html>
这里,每当执行timer方法时,都会有对该方法的两次执行的引用[对变量t引起的变量的引用]。每次执行该方法创建的数据仍然可以访问。
自行关闭不会导致内存泄漏。只是由于代码编写不规范,导致闭包中不可达的作用域仍然可达,无法通过js垃圾回收回收,造成内存泄漏。
闭包的概括定义:
红皮书中的定义:
匿名函数经常被误认为是闭包。闭包是引用另一个函数范围内的变量的函数,通常在嵌套函数中实现
匿名函数经常被误认为是闭包。闭包是引用另一个函数范围内的变量的函数,通常在嵌套函数中实现
MDN 将闭包定义为:
闭包是一个可以访问自由变量的函数。
那么什么是自由变量呢?
自由变量是在函数中使用的变量,但既不是函数参数也不是函数局部变量。
由此可见,帽子由两部分组成:
闭包=函数+函数可以访问的自由变量
权威指南定义
所以在《JavaScript权威指南》中说:从技术角度来看,所有JavaScript功能都是封闭的。
嗨,这和我们平常看到的闭包有什么不同? ?
别担心,这是理论结论。事实上,有一个实际的关闭。我们看一下汤姆叔叔翻译的关于闭包的文章中的定义:
在 ECMAScript 中,闭包适用于:
理论上:所有函数。因为它们在创建时都保存了上层上下文数据。这也适用于简单的全局变量
,因为在函数中访问全局变量就相当于访问自由变量,而此时使用的是最外层作用域。
从实用的角度来看:以下函数被视为闭包:
即使创建它的上下文被销毁,它仍然存在(例如,内部函数从父函数返回)
引用代码中的自由变量
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。