什么是闭包和作用域链?

news2024/10/6 10:37:21

1. 什么是闭包

闭包指的是那些引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的。

举个栗子,createCounter 接受一个参数 n,然后返回一个匿名函数,这个匿名函数是闭包,它可以访问外部函数 createCounter 的局部变量 n。因为这个内部函数在外部有被引用,该函数会不会被销毁,n的值也会被保存。

    function createCounter(n) {
        return function () {
            return n++

        };
    };
    const a = createCounter(1)
    a()//1
    a()//2
    function createCounter2() {
        let n = 1
        return function increment() {
            console.log(++n)
        };
    };
    const b = createCounter2()
    b()//2
    b()//3

return返回的函数那个函数外部有引用,才会被保存;而show函数每次被调用都会重新被加载。

    function createCounter3() {
        return function () {
            let n = 1
           function show(){
            console.log(++n)
           }
           show()
        };
    };
const c = createCounter3() 
    c()//2
    c()//2
    function createCounter4() {
        return function () {
            let n = 1
           return function show(){
            console.log(++n)
           }
        };
    };
    const d = createCounter4()()//show函数外部有引用
    d()//2
    d()//3

2. 作用域链

理解作用域链的创建和使用,对理解闭包非常重要。

在调用一个函数时,会为这个函数调用创建一个执行上下文,并创建一个作用域链

function compare(value1, value2) { 
 if (value1 < value2) { 
 return -1; 
 } else if (value1 > value2) { 
 return 1; 
 } else { 
 return 0; 
 } 
} 
let result = compare(5, 10); 

这里定义的 compare()函数是在全局上下文中调用的。

定义函数时,就会为它创建作用域链,预装载全局变量对象,并保存在内部的[[Scope]]中。在调用这个函数时,会创建相应的执行上下文,然后通过复制函数的[[Scope]]来创建其作用域链。

第一次调用 compare()时,会为它创建一个包含 argumentsvalue1value2活动对象,这个对象是其作用域链上的第一个对象。什么是arguments对象?

compare()作用域链上的第二个对象是全局上下文的变量对象,其中包含 this、result 和 compare。
在这里插入图片描述
全局上下文中的叫变量对象,它会在代码执行期间始终存在。比如在浏览器中是 window 对象。

函数局部上下文中的叫活动对象,只在函数执行期间存在。当函数执行完毕后,活动对象会被销毁。

函数内部代码在访问变量时,会从作用域链中查找变量。函数执行完毕后,局部活动对象会被销毁内存中就只剩下全局作用域。不过,闭包就不一样了。

在一个函数内部定义的函数会把其包含函数的活动对象添加到自己的作用域链中。
因此,在createCounter()函数中,匿名函数的作用域链中实际上包含createCounter的活动对象(也就是arguments和它的形参),所以在内部的函数可以访问到外部的参数。因为匿名函数中有对n的引用,所以执行完毕后createCounter不会被销毁。

    function createCounter(n) {
        return function () {
            return n++

        };
    };
    //1.创建函数
    const a = createCounter(1)
    //2.调用函数
    a()//1 
    a()//2
	//3.除对函数的引用,这样就可以释放内存了
	a = null

创建的createCounter 函数被保存在变量 a 中。把 a设置为等于 null 会解除对函数的引用,从而让垃圾回收程序可以将内存释放掉,作用域链也会被销毁。

3. 闭包的优缺点

闭包的优点

1. 封装性

闭包允许创建私有变量,这对于封装和隐藏实现细节非常有用。通过在函数内部定义变量,并返回一个访问这个变量的函数,可以创建一个私有作用域。

function createCounter() {
  let count = 0;

  return function() {
    count++;
    return count;
  };
}

const counter = createCounter();
console.log(counter()); // 输出 1
console.log(counter()); // 输出 2

2. 模块化开发

闭包可用于实现模块化开发,创建私有作用域,防止变量污染全局命名空间。

const module = (function() {
  let privateVariable = 'I am private';

  return {
    getPrivateVariable: function() {
      return privateVariable;
    },
    setPrivateVariable: function(value) {
      privateVariable = value;
    }
  };
})();

console.log(module.getPrivateVariable()); // 输出 'I am private'
module.setPrivateVariable('Updated value');
console.log(module.getPrivateVariable()); // 输出 'Updated value'

3. 事件处理程序

在事件处理程序中,闭包可以用来维持对外部作用域的引用,以便在事件触发时访问外部变量。

function setupEventListener() {
  let count = 0;

  document.getElementById('myButton').addEventListener('click', function() {
    count++;
    console.log(`Button clicked ${count} times`);
  });
}

setupEventListener();

4. setTimeout 和 setInterval

在定时器中使用闭包,可以保存定时器内的变量状态,而不受外部环境的影响。

function startTimer() {
  let seconds = 0;

  const timer = setInterval(function() {
    seconds++;
    console.log(`Timer: ${seconds} seconds`);
  }, 1000);

  return function stopTimer() {
    clearInterval(timer);
  };
}

const stopTimer = startTimer();
// ...一些代码后
stopTimer(); // 停止定时器

5. 在循环中使用闭包

在循环中使用闭包,可以保持对每次迭代的独立作用域,防止变量共享问题。

for (let i = 1; i <= 5; i++) {
  setTimeout(function() {
    console.log(`Delayed log: ${i}`);
  }, i * 1000);
}

6. 缓存函数结果

可以实现对函数调用结果的缓存,提高性能。

function memoize(fn) {
  const cache = {};

  return function(...args) {
    const key = JSON.stringify(args);
    if (cache[key]) {
      console.log('Result retrieved from cache');
      return cache[key];
    } else {
      const result = fn(...args);
      cache[key] = result;
      return result;
    }
  };
}

const memoizedAdd = memoize(function(x, y) {
  console.log('Performing expensive calculation');
  return x + y;
});

console.log(memoizedAdd(2, 3)); // 输出 'Performing expensive calculation' 和 5
console.log(memoizedAdd(2, 3)); // 输出 'Result retrieved from cache' 和 5

闭包的缺点

  1. 内存占用:
    • 闭包可能导致内存泄漏,因为闭包中的函数引用了外部函数的变量,导致外部函数的作用域无法被垃圾回收
  2. 性能影响:
    • 闭包的使用可能对性能产生一些影响,尤其是在涉及大量闭包的场景中。每个闭包都会创建一个新的作用域链,可能会导致额外的计算成本。
  3. 复杂性:
    • 过度使用闭包可能导致代码复杂性增加,降低代码的可读性和可维护性。在某些情况下,闭包的嵌套可能会变得难以理解。
  4. 变量共享问题:
    • 闭包可以访问外部函数的变量,这可能导致变量共享的问题。在循环中创建闭包时,需要注意变量在每次迭代中的值。
  5. 潜在的安全问题:
    • 如果闭包中的函数依赖于外部作用域的变量,并且这些变量在函数调用时可能被修改,可能会导致潜在的安全问题。

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

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

相关文章

Centos7上面部署redis

Centos7上面部署redis 编写这个部署redis&#xff0c;只是为了另一个文章入侵redis做准备&#xff0c;网上还有好多类似的文章&#xff0c;这个单纯的就是部署安装&#xff0c;并简单的测试使用以下 关联其他文章 [1]VMware上面安装部署centos7镜像系统【详细含镜像】 [2]血的教…

多集群部署中的 Kubernetes 弹性 (RTO/RPO)

啊&#xff0c;Kubernetes&#xff01;我们DevOps挑战的万灵药。 Kubernetes是一个开源的容器编排工具&#xff0c;本应加速软件交付、保护我们的应用程序、降低成本并减少我们的头痛问题&#xff0c;对吗&#xff1f; 不过说真的&#xff0c;Kubernetes已经彻底改变了我们编…

kali系统复现环境:Vulfocus 提示服务器内部错误,请联系管理员的解决方法

Linux-kali系统复现环境&#xff1a;Vulfocus&&提示服务器内部错误&#xff0c;请练习管理员的解决方法 第一步&#xff1a; 先下载docker和docker-compose apt-get update apt-get install docker apt-get install docker-compose输入如下图命令&#xff0c;有版本…

HarmonyOS应用开发者基础认证【题库答案】

HarmonyOS应用开发者高级认证【题库答案】 一、判断 首选项preferences是以Key-Value形式存储数据&#xff0c;其中Key是可以重复。&#xff08;错&#xff09;使用http模块发起网络请求时&#xff0c;必须要使用on(‘headersReceive’&#xff09;订阅请求头&#xff0c;请…

恒温恒湿试验设备故障维修指南

恒温恒湿试验设备是一种用于模拟特定温度和湿度条件下的环境的设备&#xff0c;通常用于测试产品在不同环境条件下的性能。设备可能会出现故障&#xff0c;导致温湿度控制不准确或其他问题。以下是一般的故障排除和维修指南&#xff0c;但请注意&#xff0c;具体的维修步骤可能…

01:编译lua及C调用

我们今天在windows平台编译lua&#xff0c;生成 lua动态库,lua.exe&#xff0c;luac.exe 我把这个目录上传到giee&#xff0c;使用下面命令获取它: git clone gitgitee.com:jameschenbo/lua_c_application.git 或者直接访问:访问网页 目录结构如下&#xff1a; build.cmd 是…

美女骑士开箱VELO Angel TT,银色天使,无痛骑行

阳光、女孩、自行车&#xff0c;脸上的笑容或明媚&#xff0c;或神秘&#xff0c;或青涩&#xff0c;在这个时候&#xff0c;世界上没有什么比骑行女孩更美的了&#xff01;      在北京&#xff0c;有一个热爱骑行的女孩&#xff0c;名叫季思铭&#xff0c;目前是中国农业…

开发测试利器之Fiddler网络调试工具详细安装使用教程(包含汉化脚本)

一、Fiddler简介 Fiddler 是一款功能强大的网络调试工具&#xff0c;可以帮助开发人员和测试人员分析和调试网络流量。它通过截取计算机和服务器之间的HTTP/HTTPS请求&#xff0c;并提供详细的请求和响应信息来帮助我们理解和诊断网络通信。 Fiddler 可以用于各种用途&#x…

案例说法:智能网联车背后的安全隐患

随着汽车智能化、网联化的发展&#xff0c;汽车数据处理能力日益增强&#xff0c;未经授权对个人信息和重要数据采集、利用等数据安全问题逐步凸显。对车辆网络攻击、网络侵入等网络安全问题恐将危及个人生命安全、合法权益&#xff0c;甚至危害公共利益、国家安全&#xff0c;…

【活动回顾】sCrypt在柏林B2029开发者周

B2029 是柏林的一个区块链爱好者、艺术家和建设者聚会&#xff0c;学习、讨论和共同构建比特币区块链地方。 在2023年6月9日至11日&#xff0c;举行了第7次Hello Metanet研讨会。本次研讨会旨在为参与者提供一个学习、讨论和共同构建比特币区块链的平台。 在这个充满激情和创意…

js moment时间范围拿到中间间隔时间

2023.11.27今天我学习了如何对只返回的开始时间和结束时间做处理&#xff0c;比如后端返回了&#xff1a; [time:{start:202301,end:202310}] 我们需要把中间的间隔渲染出来。 [202301,202302,202303,202304,202305,202306,202307,202308,202309,202310] 利用moment的add进…

图解算法数据结构-LeetBook-树03_层序遍历奇数偶数行方向不同

一棵圣诞树记作根节点为 root 的二叉树&#xff0c;节点值为该位置装饰彩灯的颜色编号。请按照如下规则记录彩灯装饰结果&#xff1a; 第一层按照从左到右的顺序记录 除第一层外每一层的记录顺序均与上一层相反。即第一层为从左到右&#xff0c;第二层为从右到左。 示例 1&…

需要更改你的Apple ID吗?你可以在iPhone上管理它!一文教你如何更改Apple ID

你的iCloud帐户用于将你的数据存储在苹果的云存储服务中。它还连接到你的Apple ID,该ID用于使用iPhone和iPad进行在线购买。更改你的联系信息、发货地址和Apple ID密码很容易。 如何在iPhone和iPad上更改你的Apple ID联系信息 你可以在iPhone或iPad上更改你的姓名、电子邮件…

【数据库】MongoDB 简介:一种文档数据库

MongoDB 简介&#xff1a;一种文档数据库 1.MongoDB 介绍2.MongoDB 数据存储方式3.MongoDB 概念与操作3.1 Create Operations3.2 Read Operations3.3 Update Operations3.4 Delete Operations 4.MongoDB 特点5.MongoDB 应用场景 1.MongoDB 介绍 MongoDB 是一个基于分布式文件存…

Node——Node.js简介

Node.js是一个基于Chrome V8引擎的JavaScript运行时环境&#xff0c;它能够让JavaScript脚本运行在服务端&#xff0c;这使得JavaScript成为与PHP、Python等服务端语言平起平坐的脚本语言。 1、认识Node.js Node.js是当今网站开发中非常流行的一种技术&#xff0c;它以简单易…

Eureka简单使用做微服务模块之间动态请求

创建一个eureka模块,引入eureka 为启动项加上EnableEurekaServer注解 配置信息 orderService和userService的操作是一样的 这里以orderService为例: 引入eureka客户端 加上 LoadBalanced注解 配置 orderService和userService都配置好了之后 启动 这样我们在http://localhos…

Matplotlib画图接口_Python数据分析与可视化

Matplotlib画图接口 导入matplotlib显示图像画图接口 导入matplotlib 和numpy,pandas一样&#xff0c;在导入matplotlib时我们也可以用一些常用的简写形式&#xff1a; import matplotlib as mpl import matplotlib.pyplot as pltpyplot是最常用的画图模块接口&#xff0c;功…

KaiwuDB 亮相中国 5G + 工业互联网大会,助力新型工业化

11月19-21日&#xff0c;由各相关政府部门共同主办的“2023 中国 5G工业互联网大会”在湖北武汉盛大举行。作为我国“5G工业互联网”领域的国家级顶会&#xff0c;本届大会以“数实融合&#xff0c;大力推进新型工业化”为主题&#xff0c;聚焦新型基础设施、产业转型升级、技术…

麒麟V10服务器搭建FTP服务

概念 1.1介绍 FTP&#xff1a;File transfer protocol 文件传输协议 1.2原理 默认采用被动模式 被动模式FTP 为了解决服务器发起到客户的连接的问题&#xff0c;人们开发了一种不同的FTP连接方式。这就是所谓的被 动方式&#xff0c;或者叫做PASV&#xff0c;当客户端通…

计算机组成原理-Cache替换算法

文章目录 总览随机算法&#xff08;RAND&#xff09;先进先出算法&#xff08;FIFO&#xff09;近期最少使用算法&#xff08;LRU&#xff09;最不经常使用算法&#xff08;LFU&#xff09;总结 总览 随机算法&#xff08;RAND&#xff09; 没有选择性地考虑替换哪一块Cache&a…