JS - 闭包(Closure)

news2024/9/25 19:20:54

目录

  • 1,什么是闭包
  • 2,创建闭包
  • 3,如何销毁闭包
    • 2.1,自动创建的闭包
    • 2.2,手动创建的闭包
  • 4,闭包的特点和使用场景
    • 3.1,特点
    • 3.2,使用场景
      • 避免全局变量污染
      • 函数柯里化
  • 5,闭包经典问题

闭包的定义有许多的说法,下面来介绍下我理解的。

1,什么是闭包

首先,在 js 中闭包是通过作用域链来实现的。

闭包可以看做是一个封闭的空间,用来存储当前作用域的变量,来在其他地方引用。

2,创建闭包

执行函数时,只要在函数中使用了外部数据,就会创建闭包

验证一下:

function fun() {
  const i = 1
  console.log(i) // 断点
}
fun()

在这里插入图片描述

const i = 1
function fun() {
  console.log(i)
}
fun()

在这里插入图片描述

而变量是否放入到闭包中,要看其他地方有没有对这个变量的引用。

举例:

const a = 1

function fun() {
  const b = 2
  const c = 3
  function fun1() {
    const d = 4
    const e = 5
    function fun2() {
      console.log(a, b, c, d)
    }
    fun2()
  }
  fun1()
}
fun()

在这里插入图片描述

可以看到创建了3个闭包,分别存储对应作用域的变量。

  • 全局
  • fun
  • fun1

而变量 e 并没有被放到闭包中,因为没有被引用。

3,如何销毁闭包

闭包的产生会占用空间。那如何销毁闭包,释放空间?

创建闭包分为2种情况,销毁也有所区别。

  • 自动创建的闭包
  • 手动创建的闭包

2.1,自动创建的闭包

自动创建的闭包,在函数调用完会直接销毁掉

在上面的例子中,fun() 执行完成后,变量 a 在全局环境中(没有在全局闭包中)正常输出,变量b 会报错。

fun()
console.log(a) // 1
console.log(b) // Error

在这里插入图片描述

可以看到已经没有任何闭包存在了,垃圾回收器会自动回收没有引用的变量 b,c,d,e,不会有内存被占用的情况。

2.2,手动创建的闭包

手动创建的闭包,可以设置在函数调用完依然保留

先看一个例子:

function getUser() {
  const name = '下雪天的夏风'
  console.log(name);
}
getUser()
console.log(name); // Error

很明显,局部变量 name 会随着 getUser() 的执行上下文创建而创建,销毁而销毁。所以getUser()执行完后,name 也就不存在了,打印报错。

修改代码如下:

function getUser() {
  const name = '下雪天的夏风'
  return function () {
    console.log(name)
  }
}
const user = getUser()
user() // 下雪天的夏风

在这里插入图片描述

调用 user() 为什么能访问到 name,因为垃圾回收器只会回收没有被引用的变量。原本getUser()调用完,name就会被销毁掉,但此时向外部返回了一个匿名函数,该函数引用了 name,所以name不会被垃圾回收器回收。

4,闭包的特点和使用场景

3.1,特点

  • 通过闭包可以让外部环境访问到函数内部的局部变量。
  • 通过闭包可以暂存局部变量,不随着它的上下文环境一起销毁。

因为这2个特点,也就产生了下面的使用场景:

3.2,使用场景

避免全局变量污染

在 js 还无法模块化的时期,多人协作有可能会导致定义的全局变量产生命名冲突。使用闭包可以将变量和对应的功能放到一个独立的空间中,来在一定程度上解决全局变量污染问题。

const name = '全局' // 全局变量

const init = (function () {
  const name = '局部name1'
  function callName() {
    console.log(name)
  }
  return function () {
    callName()
  }
})()
init() // 局部 name1

const initSuper = (function () {
  const name = '局部name2'
  function callName() {
    console.log(name)
  }
  return function () {
    callName()
  }
})()
initSuper() // 局部 name2

函数柯里化

参考 这篇文章

5,闭包经典问题

for (var i = 0; i < 3; i++) {
  setTimeout(function () {
    console.log(i)
  }, 1000)
}

上面的代码中,我们预期 1s 后输出 0,1,2,但实际会输出3个3。

这个问题就是因为闭包导致的。setTimeout 的回调函数访问了外部变量 i,形成闭包。而变量 i 只有1个,循环结束后,访问的变量 i 也是同一个。

解决

方式1:利用立即执行函数,实现 setTimeout 的回调函数不再访问外部变量。

for (var i = 0; i < 3; i++) {
  ;(function (index) {
    setTimeout(function () {
      console.log(index)
    }, 1000)
  })(i)
}

方式2:利用 setTimeout 的第3个参数,实现 setTimeout 的回调函数不再访问外部变量。

for (var i = 0; i < 3; i++) {
  setTimeout(
    function (index) {
      console.log(index)
    },
    1000,
    i
  )
}

// 或
for (var i = 0; i < 3; i++) {
  setTimeout(console.log, 1000, i)
}

方式3:利用 let 关键字产生的块级作用域,让每次循环都是新的变量i

for (let i = 0; i < 3; i++) {
  setTimeout(function () {
    console.log(i)
  }, 1000)
}

注意是块级作用域的原因,此时并没有产生闭包。

在这里插入图片描述


以上。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1324076.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

万兆网络之疑难杂症(一)

症状&#xff1a;电话线测线仪4芯全亮&#xff0c;插上话机不亮 由于装修方没有按要求布线&#xff0c;导致没有电话线用&#xff0c;因此分网线用于电话线 测试网线8芯全亮&#xff0c;分四芯用端子接电话线&#xff0c;再压电话线水晶头&#xff0c;再测水晶头全亮&#xf…

如何解决苹果应用商城审核拒绝的Guideline 2.1 - Information Needed问题

当你的应用程序在苹果应用商城审核过程中被拒绝时&#xff0c;苹果会向您发送一封邮件&#xff0c;其中提供了关于拒绝原因的详细信息。本文将指导您如何正确处理Guideline 2.1 - Information Needed问题&#xff0c;并提供解决方案&#xff0c;以确保您的应用程序能够通过审核…

WINDOWS(WIN11)通过IP添加网络打印机

点击添加设备 点击手动添加 使用IP地址或主机名添加打印机 选择TCP/IP设备&#xff0c;输入打印机地址 如果有正确驱动就安装&#xff0c;没有就取消。 通过手动设置添加本地打印机或网络打印机 使用现有的端口 根据打印机IP&#xff0c;选择标准端口。 成功&#xff01; 到…

SpringBoot代码混淆与反混淆加密工具详解

目录 反编译 混淆 正文 一共就两步&#xff0c;无需源码&#xff0c;直接对ipa文件进行混淆加密 打开要处理的IPA文件 设置签名使用的证书和描述文件 开始ios ipa重签名 简单就是把代码跑一哈&#xff0c;然后我们的代码 .java文件 就被编译成了 .class 文件 反编译 就是…

队列(C语言版)

一.队列的概念及结构 队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有 先进先出 FIFO(First In First Out) 入队列&#xff1a;进行插入操作的一端称为 队尾 出队列&#xff1a;进行删除操作的一端称为…

关于我对归纳偏置(inductive bias)的概念和应用的详细总结

归纳偏置&#xff08;inductive bias&#xff09; 1.归纳偏置&#xff08;inductive bias&#xff09;的概念2.归纳偏置&#xff08;inductive bias&#xff09;的应用 1.归纳偏置&#xff08;inductive bias&#xff09;的概念 归纳偏置&#xff08;inductive bias&#xff0…

如何在Ubuntu系统中安装VNC并结合内网穿透实现远程访问桌面

文章目录 前言1. ubuntu安装VNC2. 设置vnc开机启动3. windows 安装VNC viewer连接工具4. 内网穿透4.1 安装cpolar【支持使用一键脚本命令安装】4.2 创建隧道映射4.3 测试公网远程访问 5. 配置固定TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址5.3 测试…

2023 英特尔On技术创新大会直播 |让更多人了解AI魅力

2023 英特尔On技术创新大会直播 |让更多人了解AI魅力 前言&#xff1a;主要领域:人工智能&#xff1a;使用 OpenVINO™ 落地边缘端生成式 AIOpenVINO™学习总结&#xff1a; 新一代 AI PC计算平台&#xff1a;新一代至强平台&#xff1a;边云协同&#xff1a;先进技术&#xff…

前后端分离跨域问题的OPTIONS请求(预检请求)

本篇文章用于个人的问题记录 问题描述: 使用了springbootvue3做前后端分离,使用sa-token做登录认证 由于sa-token的前后端分离的登录认证需要在请求发起时自定义添加头部satoken 好那么问题来了,我请求的时候看我的请求头是存在satoken这个头部信息的 但我在springboot的拦截…

ansible在ubuntu下的安装和使用

ansible在ubuntu下的安装和使用 本文目录 ansible在ubuntu下的安装和使用安装和配置虚拟机配置安装和验证 简单使用创建 ansible cfg 和 inventory 文件创建剧本并执行使用 ansible vault 加密 安装和配置 中文文档&#xff1a;http://www.ansible.com.cn/docs/intro_installa…

操作系统快速刷题1

操作系统——内存管理之内存分配&#xff08;分页&#xff0c;分段&#xff0c;段页&#xff09; 分页存储概念清晰梳理&#xff08;页面、页表、页表项、页面大小、页内地址等概念&#xff09; 页框&#xff0c;页表&#xff0c;页表项&#xff0c;页面大小&#xff0c;页…

Hazelcast系列(十一):Map(三)备份、过期驱逐与内存格式

系列文章 Hazelcast系列(一)&#xff1a;初识hazelcast Hazelcast系列(二)&#xff1a;hazelcast集成&#xff08;嵌入式&#xff09; Hazelcast系列(三)&#xff1a;hazelcast集成&#xff08;服务器/客户端&#xff09; Hazelcast系列(四)&#xff1a;hazelcast管理中心 …

四川云汇优想教育咨询有限公司电商服务靠谱吗

随着抖音电商的兴起&#xff0c;越来越多的商家开始关注这一领域。四川云汇优想教育咨询有限公司作为一家专注于电商服务的企业&#xff0c;也受到了广泛的关注。那么&#xff0c;四川云汇优想教育咨询有限公司的抖音电商服务靠谱吗&#xff1f;下面我们将从多个方面进行深入剖…

OpenCV技术应用(9)— 视频的暂停播放和继续播放

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。本节课就手把手教大家如何控制视频的暂停播放和继续播放&#xff0c;希望大家学习之后能够有所收获~&#xff01;&#x1f308; 目录 &#x1f680;1.技术介绍 &#x1f680;2.实现代码 &#x1f680;1.技术介绍…

Leetcode—96.不同的二叉搜索树【中等】

2023每日刷题&#xff08;六十四&#xff09; Leetcode—96.不同的二叉搜索树 算法思想 实现代码 class Solution { public:int numTrees(int n) {vector<int> G(n 1, 0);G[0] 1;G[1] 1;for(int i 2; i < n; i) {for(int j 1; j < i; j) {G[i] G[j - 1] * …

appium工具相关

一、appium基本介绍 1、appium 基本介绍 定义&#xff1a;appium 就是一款非常流行和好用的第三方工具&#xff0c;通过该工具我们可以配合 python 脚本实现 IOS / Android 多平台的APP 自动化测试。作用&#xff1a;在编写测试脚本的PC机和运行 APP 的真机或设备之前充当一个…

Linux Mint 21.3 代号为“Virginia”开启下载

Linux Mint 团队今天放出了 Linux Mint 21.3 Beta ISO 镜像&#xff0c;正式版计划在今年圣诞节发布。 支持 在实验性支持 Wayland 之外&#xff0c;Cinnamon 6.0 版 Linux Mint 21.3 Beta 镜像还带来了其它改进&#xff0c;Nemo 文件夹管理器右键菜单支持下载相关操作。 Cin…

一个很好用的Docker可视化管理工具

目录 前言Portainer安装部署使用 前言 一个好的docker可视化管理工具&#xff0c;可以提升我们不少的工作效率&#xff0c;下面我就推荐一个我使用过的&#xff0c;感觉很不错的一个可视化管理工具给大家 Portainer Portainer是一个开源的Docker管理工具&#xff0c;提供了容…

WPF——样式和控件模板、数据绑定与校验转换

样式和控件模板 合并资源字典 Style简单样式的定义和使用 ControlTemplate控件模板的定义和使用 定义 使用 Trigger触发器 数据绑定与校验转换 数据绑定的设置 代码层实现绑定 数据模板DataTemplate xml文件的读取与显示 方法的返回值作为源绑定到控件中ObjectDataProvider L…