JS面试题之---解释一下什么是闭包?

news2024/11/14 3:29:17
  • 闭包:闭包是一个函数,它能够访问并引用其外部函数作用域中的变量,即使外部函数已经执行完毕。这种特性使得闭包可以保持对外部变量的引用,从而在函数外部也能访问和修改这些变量。
function createCounter() {  
    let count = 0; // 这是外部函数的局部变量  

    return function() { // 返回的内部函数形成了闭包  
        count += 1; // 内部函数可以访问并修改外部函数的局部变量  
        return count; // 返回当前的计数值  
    };  
}  

const counter = createCounter(); // 创建一个计数器  

console.log(counter()); // 输出: 1  
console.log(counter()); // 输出: 2  
console.log(counter()); // 输出: 3  

//这个例子展示了闭包的一个典型用途:在 JavaScript 中实现数据的私有性,通过创建函数工厂生成具有独立状态的计数器。闭包使得我们可以在内部函数中访问和操作外部函数的变量,即使外部函数已经返回并结束。
  • 闭包需要满足三个条件:
    1. 函数嵌套:闭包必须是一个内部函数,即一个函数内部定义了另一个函数。
    2. 引用外部函数变量:内部函数可以访问和修改外部函数的变量。
    3. 延续外部函数的生命周期:即使外部函数已经执行完毕,内部函数仍然可以访问外部函数的变量。
function outerFunction() {  
    let outerVariable = 'I am from the outer function!'; // 外部函数的局部变量  

    // 条件1: 函数嵌套  
    function innerFunction() {  
        // 条件2: 访问外部变量  
        console.log(outerVariable);  
    }  

    return innerFunction; // 返回内部函数  
}  

const closureFunction = outerFunction(); // 执行外部函数并返回内部函数  
closureFunction(); // 输出: I am from the outer function!


//解释:
//函数嵌套:innerFunction 是定义在 outerFunction 内部的。
//访问外部变量:innerFunction 中可以访问并输出 outerVariable,即使执行 outerFunction 已经结束。
//延续外部变量的生命周期:即使 outerFunction 的执行上下文已经结束,但 innerFunction 仍然保持对 outerVariable 的引用,允许我们在调用 closureFunction() 时访问 outerVariable。
  • 优点: 可以重复使用变量,并且不会造成变量污染。
  • 缺点: 会引起内存泄漏
    • 内存泄漏是指程序在运行时没有及时释放不再使用的内存,导致可用内存逐渐减少,可能最终导致应用程序性能下降甚至崩溃。
    • 闭包可以持有外部作用域的变量引用,如果这些变量的生命周期超出了需要使用的范围,可能会导致内存泄漏。
    • 如果使用了某些长生命周期的数据结构(如单例模式或全局缓存)而没有适当地清理不再需要的数据,也可能导致内存泄漏。
      • 预防内存泄漏的方法:

        • 及时清理引用:确保不再需要的对象或变量被及时置为 null,允许垃圾回收机制回收内存。
        • function createClosure() {  
              let resource = { /* large object */ };  
          
              return function innerFunction() {  
                  // 使用 resource  
              };  
          }  
          
          // 当不再需要这个闭包,做清理  
          const closure = createClosure();  
          // ...使用 closure...  
          closure = null; // 切断与资源的引用

        • 避免使用全局变量:尽量减少全局变量的使用,使用模块化的代码组织方式。

        • let globalRef; // 避免使用,如有必要,考虑使用模块化方法。  
          
          function createClosure() {  
              let localResource = { /* large object */ };  
          
              return function innerFunction() {  
                  // 不要把 localResource 放入 globalRef  
              };  
          }
  •  闭包的主要用途包括:
    1. 实现数据封装和私有变量
      • 闭包能够通过函数作用域来封装变量,让这些变量不会被外部访问,从而实现数据封装和私有变量的效果。这有助于保护数据不被意外修改。
      • function createCounter() {  
            let count = 0; // 私有变量  
        
            return {  
                increment: function() {  
                    count++;  
                    return count;  
                },  
                decrement: function() {  
                    count--;  
                    return count;  
                },  
                getCount: function() {  
                    return count;  
                }  
            };  
        }  
        
        const counter = createCounter();  
        console.log(counter.increment()); // 1  
        console.log(counter.increment()); // 2  
        console.log(counter.getCount());  // 2
    2. 实现函数柯里化
      • 函数柯里化是将多个参数的函数转换为一系列单一参数的函数链,闭包可以用来保持对初始参数的访问。
      • function curriedSum(a) {  
            return function(b) {  
                return a + b;  
            };  
        }  
        
        const addFive = curriedSum(5);  
        console.log(addFive(10)); // 15
        
        //curriedSum 函数返回一个新函数,该新函数闭包当前的 a 值。这样你可以逐步传递参数。
    3. 实现函数防抖和节流
      • 防抖(Debounce):多次触发 只执行最后一次。
        • function debounce(func, delay) {  
              let timeout;  
              return function(...args) {  
                  clearTimeout(timeout);  
                  timeout = setTimeout(() => func.apply(this, args), delay);  
              };  
          }  
          
          const handleResize = debounce(() => {  
              console.log('Window resized!');  
          }, 200);  
          window.addEventListener('resize', handleResize);
      • 节流(Throttle):规定时间内 只触发一次。
        • function throttle(func, limit) {  
              let lastFunc;  
              let lastRan;  
              return function(...args) {  
                  if (!lastRan) {  
                      func.apply(this, args);  
                      lastRan = Date.now();  
                  } else {  
                      clearTimeout(lastFunc);  
                      lastFunc = setTimeout(() => {  
                          if ((Date.now() - lastRan) >= limit) {  
                              func.apply(this, args);  
                              lastRan = Date.now();  
                          }  
                      }, limit - (Date.now() - lastRan));  
                  }  
              };  
          }  
          
          const logScroll = throttle(() => {  
              console.log('Scroll event triggered!');  
          }, 1000);  
          window.addEventListener('scroll', logScroll);
    4. 实现缓存机制
      • 闭包可以存储和缓存计算结果,从而提高程序效率,尤其是在频繁调用某个计算开销大的函数时。
      • function memoize(fn) {  
            const cache = {};  
            return function(...args) {  
                const key = JSON.stringify(args);  
                if (cache[key]) {  
                    return cache[key]; // 从缓存返回结果  
                }  
                const result = fn.apply(this, args);  
                cache[key] = result; // 保存到缓存  
                return result;  
            };  
        }  
        
        const factorial = memoize(function(n) {  
            if (n === 0) return 1;  
            return n * factorial(n - 1);  
        });  
        
        console.log(factorial(5)); // 120  
        console.log(factorial(5)); // 从缓存返回 120
  • 使用闭包的注意点:
    1. 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
    2. 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象
      object )使用,把闭包当作它的公用方法( Public Method ),把内部变量当作它的私有属性 (private value ),这时一定要小心,不要随便改变父函数内部变量的值。

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

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

相关文章

[CKS] K8S AppArmor Set Up

最近准备花一周的时间准备CKS考试,在准备考试中发现有一个题目关于AppArmor Pod操作权限的问题。 ​ 专栏其他文章: [CKS] Create/Read/Mount a Secret in K8S-CSDN博客[CKS] Audit Log Policy-CSDN博客 -[CKS] 利用falco进行容器日志捕捉和安全监控-CSDN博客[CKS] …

Tomcat漏洞利用工具-TomcatScanPro(二)

项目地址 https://github.com/lizhianyuguangming/TomcatScanPro 简介 本项目是一个针对 Tomcat 服务的弱口令检测、漏洞检测以及本地文件包含(LFI)攻击工具。除了支持 CVE-2017-12615 漏洞的多种利用方式外,新版本还集成了 CNVD-2020-104…

【XML协议】轻松掌握使用C++ XML解析库——pugixml

文章介绍了xml协议的组成以及C xml解析库pugixml的常用操作。源于开发中每次遇到xml操作时,都要回过头查看pugixml库常用操作时什么样的,能不能有个更深刻和清晰的认识呢?其实搞清楚xml结构和pugixml组织结构的对照关系,以及pugix…

《无线重构世界》射频模组演进

射频前端四大金刚 射频前端由PA、LNA、滤波器、开关“四大金刚” 不同的模块有自己的工艺和性能特点 分层设计 射频前端虽然只由PA、LNA、开关、混频器4个模块构成,但不同模块之间相互连接且相互影响。如果将射频系统当成一个整体来理解,其中的细节和…

《Python编程实训快速上手》第五天--模式匹配与正则表达式

一、不用正则表达式查找文本模式 文本模式是一种人为规定的结构,现在有一个模式:3个数字-3个数字-4个数字 使用isPhoneNumber()函数来判断字符串是否匹配该模式 def isPhoneNumber(number):if len(number) ! 12:return Falsefor i in range(0,3):if n…

文件读写函数(1)

大家好,今天我们来介绍一下文件读写函数,昨天我们已经简单提及了一下文件的概念,那么我们今天就不多说,下面我们来看函数。 1.fopen函数 这个函数是用来打开文件的,它的两个参数分别是文件名和文件的打开模式&#x…

华为数通HCIA系列第5次考试-【2024-46周-周一】

文章目录 1、子网掩码有什么作用,和IP地址是什么关系,利用子网掩码可以获取哪些信息?2、已知一个IP地址是192.168.1.1,子网掩码是255.255.255.0,求其网络地址3、已知某主机的IP地址是192.168.100.200,子网掩…

Linux(CentOS)运行 jar 包

1、在本地终端运行,关闭终端,程序就会终止 java -jar tlias-0.0.1-SNAPSHOT.jar 发送请求,成功 关闭终端(程序也会终止) 发送请求,失败 2、在远程终端运行,关闭终端,程序就会终止 …

GIT:如何查找已删除的文件的历史记录

首先你得知道文件的名称和路径 然后打开 gitlab,到项目中,仓库-> 文件 查找文件 复制文件名到可能存在过这个文件的分支当中,就能看到了

C++builder中的人工智能(21):Barabási–Albert model(BA)模型

在此之前,大多数网络被想当然的认为是随机的,因此连接度分布可以近似用泊松分布来表示,而巴拉巴西与其学生阿尔伯特、郑浩雄通过对万维网度分布测量的结果却显示万维网度分布服从幂律分布,存在枢纽节点(拥有大量链接的…

新手 Vue 项目运行

前言:前面讲了我们已经将spingboot项目运行起来了,现在我们只需将后台管理的Vue项目运行起来即可完成整个项目。 在运行vue项目之前,请先运行springboot项目,运行步骤请看:运行Springboot Vue 项目_springbootvue项目…

AUTOSAR_EXP_ARAComAPI的7章笔记(2)

☞返回总目录 相关总结:服务发现实现策略总结 7.2 服务发现的实现策略 如前面章节所述,ara::com 期望产品供应商实现服务发现的功能。服务发现功能基本上是在 API 级别通过 FindService、OfferService 和 StopOfferService 方法定义的,协议…

计算机网络分析题

网络的布置 根据具体需求布置网络 第二小题、网络的划分 根据路由表作出路由器拓扑图 ARP跨网络寻址 TCP报文段格式概念 网桥的转发表与动作 网络嗅探报文 十六进制化作十进制 嗅探以太网帧首部 除MAC帧以外,其他各层协议数据单元都是源地址在前,目…

【初阶数据结构与算法】线性表之链表的分类以及双链表的定义与实现

文章目录 一、链表的分类二、双链表的实现1.双链表结构的定义2.双链表的初始化和销毁初始化函数1初始化函数2销毁函数 3.双链表的打印以及节点的申请打印函数节点的申请 4.双链表的头插和尾插头插函数尾插函数 5.双链表的查找和判空查找函数判空函数 6.双链表的头删和尾删头删函…

【AI写作宝-注册安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被黑客攻击,存在如下安全问题: 1. 暴力破解密码,造成用户信息泄露 2. 短信盗刷的安全问题,影响业务及导致用户投诉 3. 带来经济损失,尤其是后付费客户,风险巨大,造…

【HarmonyNext】显示提示文字的方法

【HarmonyNext】显示提示文字的方法 本文介绍在 HarmonyNext 中显示提示文字的两种常见方法:使用自定义弹窗 CustomDialog 和使用 promptAction 的 showToast 方法。 一、使用自定义弹窗 CustomDialog 在 HarmonyNext 中,自定义弹窗是实现复杂提示信…

【3D Slicer】的小白入门使用指南

一、3D Slicer认识 3D Slicer是一个开源医学影像分析和可视化平台(本质是TotalSegmentator的软件版)。(补充:TotalSegmentator 是一个用于医学图像分割的开源工具,能够对104种解剖结构进行精确分割。该项目基于深度学习技术,支持CT和MR图像的处理。TotalSegmentator 提供…

ts定义接口返回写法

接口&#xff08;未进行ts定义&#xff09; export async function UserList(params: {// keyword?: string;current?: number;pageSize?: number;},// options?: { [key: string]: any }, ) {return request<API1.UserList>(http://geek.itheima.net/v1_0/mp/artic…

.NET Core 应用程序如何在 Linux 中创建 Systemd 服务 ?

.NET Core 和 Linux 已经成为一个强大的组合&#xff0c;为开发人员提供了一个灵活、高性能的平台来构建和运行应用程序。在 Linux 上部署 .NET Core 应用程序的一个关键方面是利用 systemd 服务来确保应用程序顺利运行&#xff0c;在开机时自动启动&#xff0c;并在失败后重新…

低代码平台总览

低代码平台&#xff08;Low-Code Platform&#xff09;是一种软件开发工具&#xff0c;它允许用户通过图形化界面和少量的编码来快速构建应用程序。低代码平台的核心理念是通过抽象和最小化手工编码的方式&#xff0c;加速软件开发和部署的过程。以下是低代码平台的一些关键特性…