JavaScript 中闭包是什么?有哪些应用场景?

给大家推荐一个实用面试题库

1、前端面试题库 (面试必备)            推荐:★★★★★

地址:web前端面试题库

闭包是什么?

  • 闭包是指一个函数可以访问并操作其词法作用域外的变量的能力。
  • 闭包就是能够读取其他函数内部变量的函数。
  • 例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。
  • 在本质上,闭包是将函数内部和函数外部连接起来的桥梁。

特点:函数嵌套,并返回子函数,子函数访问了外变量。

//外部函数
function outerFunction() {//内部函数外的变量var outerVariable = 'I am from outer function'; //返回内部函数function innerFunction() { console.log(outerVariable); } return innerFunction; 
} // outerFunction执行完之后被销毁,但是outerVariable被引用着所以仍然活着
var closure = outerFunction(); closure(); // 输出:I am from outer function

闭包的作用?

  1. 封装私有变量:闭包可以用于创建私有变量和方法。通过在函数内部定义变量,并返回一个内部函数,外部无法直接访问这些变量,从而实现了封装的效果。这样可以避免全局变量的污染,提高代码的可维护性和安全性。
  2. 延迟执行:闭包可以用于实现延迟执行的效果。通过在函数内部定义一个定时器或事件监听器,并返回一个内部函数,可以在需要的时候触发执行。
  3. 记忆化/保持状态:闭包可以用于实现记忆化的效果,即将函数的计算结果缓存起来,以便在后续调用时直接返回缓存的结果,提高函数的执行效率。
  4. 回调函数:闭包可以用于实现回调函数。通过将一个函数作为参数传递给另一个函数,并在内部函数中调用该函数,可以实现异步操作的回调机制。
  5. 模块化开发:闭包可以用于实现模块化开发。通过将一组相关的变量和方法封装在一个闭包中,可以避免全局命名空间的污染,实现模块的独立性和复用性。

闭包的缺陷?

  1. 内存占用:闭包会导致外部函数的变量无法被垃圾回收,从而增加内存占用。如果闭包会长时间存在,那么外部变量将无法被释放,可能导致内存泄漏。
  2. 性能损耗:闭包涉及到作用域链的查找过程,会带来一定的性能损耗。在性能要求高的场景下,需要注意闭包的使用。

闭包的应用场景?

参考链接: js闭包的6种应用场景!!!这下会用了

1. 自执行函数(可以实现单例模式)
let say = (function(){let val = 'hello world';function say(){console.log(val);}return say;
})()
var Singleton = (function () {var instance;function createInstance() {var object = new Object("I am the instance");return object;}return {getInstance: function () {if (!instance) {instance = createInstance();}return instance;},};})();
2. 防抖节流
// 节流函数封装
function throttle(func, delay) {let timer = null;return function () {if (!timer) {timer = setTimeout(() => {func.apply(this, arguments);timer = null;}, delay);}};
}// 防抖函数封装
function debounce(func, delay) {let timer = null;return function () {clearTimeout(timer);timer = setTimeout(() => {func.apply(this, arguments);}, delay);};
}
3. 函数柯里化
//柯里化前
function add(a, b, c) {return a + b + c;
}
console.log(add(1, 2, 3)); //6//柯里化后
function addCurried1(a) {return function (b) {return function (c) {return a + b + c;};};
}//箭头函数简写
const addCurried2 = (a) => (b) => (c) => a + b + c;
console.log(addCurried1(1)(2)(3)); //6
console.log(addCurried2(1)(2)(3)); //6
4. 发布订阅
function createPubSub() {// 存储事件及其对应的订阅者const subscribers = {};// 订阅事件function subscribe(event, callback) {// 如果事件不存在,则创建一个新的空数组if (!subscribers[event]) {subscribers[event] = [];}// 将回调函数添加到订阅者数组中subscribers[event].push(callback);}// 发布事件function publish(event, data) {// 如果事件不存在,则直接返回if (!subscribers[event]) {return;}// 遍历订阅者数组,调用每个订阅者的回调函数subscribers[event].forEach((callback) => {callback(data);});}// 返回订阅和发布函数return {subscribe,publish,};
}// 使用示例
const pubSub = createPubSub();// 订阅事件
pubSub.subscribe("event1", (data) => {console.log("订阅者1收到事件1的数据:", data);
});pubSub.subscribe("event2", (data) => {console.log("订阅者2收到事件2的数据:", data);
});// 发布事件
pubSub.publish("event1", "Hello");
// 输出: 订阅者1收到事件1的数据: HellopubSub.publish("event2", "World");
// 输出: 订阅者2收到事件2的数据: World
5. 迭代器
function createIterator(arr) {let index = 0;return {next: function() {if (index < arr.length) {return {value: arr[index++],done: false};} else {return {done: true};}}};
}const myIterator = createIterator([1, 2, 3]);console.log(myIterator.next()); // { value: 1, done: false }
console.log(myIterator.next()); // { value: 2, done: false }
console.log(myIterator.next()); // { value: 3, done: false }
console.log(myIterator.next()); // { done: true }

如何释放闭包?

释放闭包通常是通过解除对闭包的引用来实现的。当不再需要使用闭包时,可以将闭包所在的变量设置为 null 或者将闭包所在的变量赋予其他值,从而断开对闭包的引用。这样,JavaScript 引擎在下一次垃圾回收时会判断闭包不再被引用,从而释放闭包占用的内存空间。

相关概念

函数的词法作用域(也叫静态作用域/闭包作用域)

是指在函数定义时确定的作用域,而不是在函数调用时确定的作用域。它是由函数在定义时所处的上下文环境决定的,与函数的调用位置无关。

规则

  • 函数内部可以访问函数外部的变量
  • 函数内部的变量在函数外部不可访问
  • 函数内部可以访问函数外部的函数
  • 函数内部的函数可以访问外部函数的变量

词法作用域的优势在于它提供了更可靠和可预测的变量访问方式。在函数定义时,就确定了函数内部可以访问的变量,不会受到函数调用位置的影响。这种静态作用域的特性使得代码更易于理解和维护,并且可以实现一些高级的编程技巧,如闭包和模块化开发。

执行上下文

每当 JavaScript 代码执行时,都会创建一个执行上下文(是 JavaScript 引擎内部的一种数据结构,用于管理代码的执行环境、变量的作用域),并按照特定的规则进行管理和销毁。

执行上下文可以分为三种类型:
  1. 全局执行上下文(Global Execution Context):全局执行上下文是在整个脚本文件执行时创建的,它是最外层的执行上下文。在全局执行上下文中,变量和函数声明会被提升,并且会创建全局对象(如浏览器环境中的 window 对象)和全局变量。
  2. 函数执行上下文(Function Execution Context):每当函数被调用时,都会创建一个函数执行上下文。函数执行上下文中包含了函数的参数、局部变量、函数内部的变量和函数声明。每个函数执行上下文都有自己的作用域链和 this 值。
  3. Eval 执行上下文(Eval Execution Context):在使用 eval() 函数执行代码时,会创建一个 eval 执行上下文。它与全局执行上下文类似,但是它有自己的词法作用域。
执行上下文的生命周期包括以下阶段:
  1. 创建阶段(Creation Phase):在这个阶段,JavaScript 引擎会创建执行上下文,并进行变量和函数的声明。变量会被初始化为 undefined,函数会被存储在内存中。
  2. 执行阶段(Execution Phase):在这个阶段,JavaScript 引擎会按照代码的顺序执行语句,给变量赋值,执行函数调用等操作。
观察和理解执行上下文

执行上下文的管理和切换由 JavaScript 引擎自动完成,开发者可以通过了解执行上下文的概念和规则,更好地理解代码的执行过程,以及变量和函数的作用范围。

尽管无法直接访问或输出执行上下文,但我们可以通过一些间接的方式来观察和理解执行上下文的行为和特性:

  1. 使用 console.log():可以在代码中使用 console.log() 方法输出变量的值、函数的执行结果等信息,从而间接观察到执行上下文的影响。
  2. 使用调试工具:现代的浏览器和开发工具提供了强大的调试功能,可以在代码执行过程中查看执行上下文的变化、变量的值以及调用栈等信息。
  3. 使用闭包:通过创建闭包,我们可以间接地观察到执行上下文的作用。闭包可以让内部函数访问外部函数的变量,从而形成一个包含外部执行上下文的闭包执行上下文。

给大家推荐一个实用面试题库

1、前端面试题库 (面试必备)            推荐:★★★★★

地址:web前端面试题库

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

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

相关文章

双轮差速模型机器人通过线速度、角速度计算机器人位姿

已知上一时刻机器人位置P_OLD (x,y,),机器人当前时刻的线速度和角速度&#xff08;v,&#xff09;,短时间内t内&#xff0c;机器人在线性部分和非线性部分的增量为 线性部分&#xff1a; 非线性部分&#xff1a; 由于可能非常小&#xff0c;导致非线性部分数值不稳定&#xf…

【R统计】各式各样的插补法解决数据缺失的问题!

&#x1f482; 个人信息&#xff1a;酷在前行&#x1f44d; 版权: 博文由【酷在前行】原创、需要转载请联系博主&#x1f440; 如果博文对您有帮助&#xff0c;欢迎点赞、关注、收藏 订阅专栏&#x1f516; 本文收录于【R统计】&#xff0c;该专栏主要介绍R语言实现统计分析的…

【计算机视觉】对极几何

文章目录 一、极线约束&#xff08;Epipolar Constraint&#xff09;二、相机标定过的情况三、相机没有标定过的情况四、八点算法&#xff08;eight-point algorithm&#xff09; 我的《计算机视觉》系列参考UC Berkeley的CS180课程&#xff0c;PPT可以在课程主页看到。 在上一…

关于preempt count的疑问

Linux中的preempt_count - 知乎 https://www.cnblogs.com/hellokitty2/p/15652312.html LWN&#xff1a;关于preempt_count()的四个小讨论&#xff01;-CSDN博客 主要是参考这些文章 之前一直认为只要是in_interrupt()返回非0值&#xff0c;那么就可以认为当前在中断上下文。即…

模拟官网编写自定义Grafana Dashboard

前言 我们想编写自定义的Dashboard&#xff0c;类似于官网那样下载的Dashboard&#xff0c;并且能移值到机器主机&#xff0c;如何实现了&#xff1f; ## 官网dashboard https://grafana.com/grafana/dashboards/ 编写 先在虚拟机写好Dashboard 然后下载。json文件如下: {…

阿昌教你如何优雅的数据脱敏

阿昌教你如何优雅的数据脱敏 Hi&#xff0c;我是阿昌&#xff0c;最近有一个数据脱敏的需求&#xff0c;要求用户可自定义配置数据权限&#xff0c;并对某种类型数据进行脱敏返回给前端 一、涉及知识点 SpringMVCJava反射Java自定义注解Java枚举 二、方案选择 1、需求要求…

Webpack打包图片-js-vue

文章目录 一、Webpack打包图片1.加载图片资源的准备2.认识asset module type3.asset module type的使用4.url-loader的limit效果 二、babel1.为什么需要babel2.babel命令行的使用3.babel插件的使用4.babel的预设preset5.babel-loader6.babel-preset 三、加载Vue文件1.编写App.v…

使用Ansible中的playbook

目录 1.Playbook的功能 2.YAML 3.YAML列表 4.YAML的字典 5.playbook执行命令 6.playbook的核心组件 7.vim 设定技巧 示例 1.Playbook的功能 playbook 是由一个或多个play组成的列表 Playboot 文件使用YAML来写的 2.YAML #简介# 是一种表达资料序列的格式,类似XML #特…

开关电源测试过压保护的测试标准及其方法

过压保护的原理 过压保护是电压超过预定值时降低电压的一种方式&#xff0c;原理是通过电路中的电压检测电路来检测电路中的电压是否超过了设定的阈值&#xff0c;如果超过了阈值&#xff0c;就会触发过压保护器件&#xff0c;使电源断开或使受控设备电压降低&#xff0c;保护电…

54.RabbitMQ快速实战以及核心概念详解

MQ MQ&#xff1a;MessageQueue&#xff0c;消息队列。这东西分两个部分来理解&#xff1a; 队列&#xff0c;是一种FIFO 先进先出的数据结构。 消息&#xff1a;在不同应用程序之间传递的数据。将消息以队列的形式存储起来&#xff0c;并且在不同的应用程序之间进行传递&am…

网络协议--TCP的交互数据流

19.1 引言 前一章我们介绍了TCP连接的建立与释放&#xff0c;现在来介绍使用TCP进行数据传输的有关问题。 一些有关TCP通信量的研究如[Caceres et al. 1991]发现&#xff0c;如果按照分组数量计算&#xff0c;约有一半的TCP报文段包含成块数据&#xff08;如FTP、电子邮件和U…

使用Fiddler进行Mock测试

1、接口抓包 找到要mock的接口&#xff0c;打开fiddler抓包 以某某接口为例&#xff0c;找到下面的接口 http://XXX/SYSTEMS 2、复制该接口数据到本地 在接口上进行右键点击&#xff0c;选择save -> …and Open as Local File -> 默认会保存至桌面&#xff0c;示例中的数…

uniapp的启动页、开屏广告

uniapp的启动页、开屏广告 启动页配置广告开屏 启动页配置 在manifest.json文件中找到APP启动界面配置&#xff0c;可以看到有Android和iOS的启动页面的配置 &#xff0c;选择自定义启动图即可配置 广告开屏 在pages中新建一个广告开屏文件并在pases.json的最顶部配置这个页…

开发商城系统的一些小建议

电子商务的迅猛发展&#xff0c;商城系统已经成为了企业推广产品和服务、吸引更多消费者的重要工具。然而&#xff0c;要想在竞争激烈的市场中脱颖而出&#xff0c;提升用户体验成为了至关重要的一环。下面就商城系统的开发作一些简单分享&#xff0c;以帮助企业更好地满足用户…

numpy矩阵索引中的省略号和冒号

numpy是Python中最常见的矩阵操作工具。有时候&#xff0c;我们需要从一个大矩阵中读取一个小矩阵&#xff0c;则需要用一些索引技巧。 我们看一个例子&#xff1a; import numpy as npa np.array([[1, 2, 3],[4, 5, 6],[7, 8, 9]])b a[:, 0] print(b) # [1 4 7]c a[..., …

跨国文件传输为什么要用专业的大文件传输软件?

跨国文件传输是许多跨国企业需要的基础工作&#xff0c;对于传输的质量和速度要求也是很严格的&#xff0c;随着数据量的不断增加&#xff0c;寻常传统的传输方式肯定是不行&#xff0c;需要新的技术和方式来进行传输&#xff0c;大文件传输软件应运而出&#xff0c;那它有什么…

【机器学习】KNN算法-模型选择与调优

KNN算法-模型选择与调优 文章目录 KNN算法-模型选择与调优1. 交叉验证2. 超参数搜索-网格搜索&#xff08;Grid Search&#xff09;3. 模型选择与调优API4. 鸢尾花种类预测-代码和输出结果5. 计算距离 问题背景&#xff1a;KNN算法的K值不好确定 1. 交叉验证 交叉验证&#x…

联想百应:构建“生态资源池”,打造中小企业转型第一服务平台

与3800多家服务商和100多家SaaS生态伙伴携手&#xff0c;累计支持超过20万中小企业智能化转型……在近日由工业和信息化部和安徽省举办的2023全国中小企业数字化转型大会上&#xff0c;联想集团首次公布供应链、平台、技术、生态与绿色赋能五大赋能能力和助力中小企业“链式”成…

sqlite3 关系型数据库语言 SQL 语言

SQL(Structured Query Language)语言是一种结构化查询语言,是一个通用的,功能强大的关系型数据库操作语言. 包含 6 个部分: 1.数据查询语言(DQL:Data Query Language) 从数据库的二维表格中查询数据,保留字 SELECT 是 DQL 中用的最多的语句 2.数据操作语言(DML) 最主要的关…

js编写一个函数判断所有数据类型

一、typeof 在 JavaScript 里使用 typeof 来判断数据类型&#xff0c;只能区分基本类型&#xff0c;即 “number”&#xff0c;”string”&#xff0c;”undefined”&#xff0c;”boolean”&#xff0c;”object” 五种。 对于数组、对象来说&#xff0c;其关系错综复杂&…