Code前端首页关于Code前端联系我们

Node.js - 通过一些示例了解闭包

terry 2年前 (2023-09-08) 阅读数 130 #Vue

必须包含在返回函数具有其自己独立可访问的外部作用域中。

实施范围和背景

js – 范围字符串
js – 执行上下文

我们从简单的例子开始

function wrapperFunction() {
  let name = 1;
  
  return function () {
    console.log(name);
  }
}

let fuc = wrapperFunction()

fuc()
 

这里的fuc范围构成了一个必要包的简单环境。

最大的特点是func目前只能访问wrapperFunction的环境变量。你可能会说wrapperFunction可以访问自己,但是当wrapperFunction执行时,这是一个具有权限的不同环境。因为func的环境变量是依赖内存来存储wrapperFunction变量的。

image.pngimage.png

碧宝什么时候消失

变量的寿命取决于盖子的寿命。闭包覆盖的外部作用域中的变量将一直存在,直到闭包函数被销毁。如果多个闭包引用一个变量,则在收集所有闭包之前,该变量不会被销毁。

因此,当不再使用必要的包时,我们将其删除。

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>
 

image.pngimage.png
这里,每当执行timer方法时,都会有对该方法的两次执行的引用[对变量t引起的变量的引用]。每次执行该方法创建的数据仍然可以访问。

自行关闭不会导致内存泄漏。只是由于代码编写不规范,导致闭包中不可达的作用域仍然可达,无法通过js垃圾回收回收,造成内存泄漏。

闭包的概括定义:

红皮书中的定义:
匿名函数经常被误认为是闭包。闭包是引用另一个函数范围内的变量的函数,通常在嵌套函数中实现

匿名函数经常被误认为是闭包。闭包是引用另一个函数范围内的变量的函数,通常在嵌套函数中实现

MDN 将闭包定义为:

闭包是一个可以访问自由变量的函数。

那么什么是自由变量呢?
自由变量是在函数中使用的变量,但既不是函数参数也不是函数局部变量。
由此可见,帽子由两部分组成:

闭包=函数+函数可以访问的自由变量

权威指南定义
所以在《JavaScript权威指南》中说:从技术角度来看,所有JavaScript功能都是封闭的。
嗨,这和我们平常看到的闭包有什么不同? ?
别担心,这是理论结论。事实上,有一个实际的关闭。我们看一下汤姆叔叔翻译的关于闭包的文章中的定义:

在 ECMAScript 中,闭包适用于:
理论上:所有函数。因为它们在创建时都保存了上层上下文数据。这也适用于简单的全局变量
,因为在函数中访问全局变量就相当于访问自由变量,而此时使用的是最外层作用域。

从实用的角度来看:以下函数被视为闭包:
即使创建它的上下文被销毁,它仍然存在(例如,内部函数从父函数返回)
引用代码中的自由变量

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门