JavaScript 闭包

目录

一、前言

二、JavaScript 变量作用域

1、变量作用域

2、如何从函数外部访问内部变量

三、JavaScript 闭包

1、闭包的定义

2、闭包的组成

3、实例说明

四、闭包的优缺点

1、优点

2、缺点

五、一个有趣的实例(定时器与闭包)


一、前言

先来看一段代码; 

// 两数相乘
function doMultiply(a){

    return function(b){

        return `${a} * ${b} = ${a * b}`;

    }
}

// 与1相乘
const multiply1 = doMultiply(1);

// 与2相乘
const multiply2 = doMultiply(2);

// 与3相乘
const multiply3 = doMultiply(3);

// ......

for(let i = 1 ; i <= 9; i++ ){
    console.log(multiply1(i), ";",  multiply2(i), ";", multiply3(i), "......");
}

输出结果: 

思考?在上述代码中:

  • multiply1 、multiply2、multiply3是什么?
  • 它们为什么能够访问到不同的变量a?
  • 它们是如何从函数外部访问函数内部变量的?

二、JavaScript 变量作用域

1、变量作用域

在JavaScript中,变量的作用域分为两种:全局变量和局部变量;

根据JavaScript中链式作用域的特点,我们知道:

  • 函数内部可以直接读取函数外部的全局变量,
  • 函数外部无法直接读取函数内部的局部变量;
function fnOut(){
    var a = 10;
    // console.log(b);   // ReferenceError: b is not defined
    function fnIn(){
        var b = 20;
        console.log(a + b); // 30
    }
    fnIn();
}

fnOut();  

在上述代码中:

  • fnIn()内部函数可以访问到外部函数fnOut()中定义的变量a;
  • fnOut()外部函数无法访问到内部函数fnIn()中定义的变量b;

2、如何从函数外部访问内部变量

有时候,需要访问函数内部的局部变量;正常情况下,无法实现,但可以采取一些不正常的方法;

既然 fnIn() 能访问到 fnOut() 中的变量a,那如果将fnIn() 作为fnOut()的返回值返回,会怎么样呢?

function fnOut(){
    var a = 10;
    // console.log(b);   // ReferenceError: b is not defined
    return function fnIn(){
        var b = 20;
        console.log(a + b); // 30
    }
    // fnIn();
}
let res = fnOut();    
console.log(res);

 既然给fnOut()定义了返回值,则使用变量res接收,打印输出返回值res:

是前面定义的内部函数fnIn(),这跟我们的预期结果是一致的;

这样做的意义是什么???

既然res是一个函数,那便可以在外部调用它,即:

function fnOut(){
    var a = 10;
    // console.log(b);   // ReferenceError: b is not defined
    return function fnIn(){
        var b = 20;
        console.log(a + b); // 30
    }
    // fnIn();
}
let res = fnOut();    
// console.log(res);
// 调用res函数
res()

这个结果和一开始的输出结果是相同的;

这意味着:

  • 在fnOut()函数的外部,能够调用其内部函数fnIn();
  • 不仅能够访问到fnOut函数中定义的变量,更能够访问到fnIn()函数中定义的变量;

将fnOut()函数中的变量a和fnIn()函数中的变量b,都从外部传入,更容易理解:

function fnOut(a){
    // var a = 10;
    // console.log(b);   // ReferenceError: b is not defined
    return function fnIn(b){
        // var b = 20;
        console.log(a + b); // 30
    }
    // fnIn();
}
let res = fnOut(10);    
// console.log(res);
// 调用res函数
res(20)

 

在调用res时,即可传入内部函数fnIn()的参数b;

上面代码中的res,就是闭包;它不仅保存了fnIn()函数实例的引用,也保存着该函数在创建时的作环境状态,可访问的所有变量;

三、JavaScript 闭包

闭包在JavaScript中是一个非常重要的概念,它可以帮助我们实现许多高级功能。理解闭包的工作原理对于编写高效、可维护的JavaScript代码非常重要。

1、闭包的定义

闭包是一个函数和其周围的状态(词法环境)的引用捆绑在一起形成的实体;

该环境包含了这个闭包创建时作用域内的任何局部变量;

也就是说,闭包让函数有了访问其创建时的作用域的能力

2、闭包的组成

  • 函数:一个普通的函数。
  • 环境:函数创建时的作用域,包括所有可访问的变量。

3、实例说明

function doActivity(name){

    var actName = `周末的活动有${name}`;

    return function(time){

        var actTime = `用了${time}小时`;

        return actName + ',' + actTime;

    }
}


let activity1 = doActivity("打游戏") ;

let activity2 = doActivity("看综艺");

let activity3 = doActivity("写代码");

console.log(activity1(2));

console.log(activity2(3));

console.log(activity3(5));

在上述示例中:

  • 定义了一个doActivity()函数,接收一个参数name,并且返回一个新的函数;返回的函数接收一个time参数,并返回处理后的值;
  • 使用doActivity()函数创建了三个新函数,分别输出每项活动的进行时间(传入的参数);
  • activity1、activity2、activity3,这三个函数都是闭包,是执行doActivity()函数时创建的内部函数实例的引用,以及词法环境的引用;
  • 他们虽然有共同的函数定义,但保存了不同的词法环境,即保存了不同的 name 与 actName 的值;

四、闭包的优缺点

1、优点

数据封装和私有化:闭包可以用来模拟私有方法和变量,使得这些变量只能通过闭包内部定义的函数来访问,从而实现数据封装和隐藏。

维持状态:闭包可以维持函数的状态,即使外部函数已经执行完毕,闭包内部定义的函数仍然可以访问外部函数的局部变量,这使得状态可以在多次函数调用之间保持。

模块化代码:闭包可以帮助创建模块,通过返回一个包含多个方法和变量的对象,这些方法和变量被封装在一个闭包内部,外部无法直接访问。

2、缺点

内存泄漏:闭包会存储其创建时的作用域,如果作用域的变量不再被使用且未被清除,会增大内存小号,导致内存泄漏;

影响性能:闭包可能会影响代码的性能,尤其是在频繁创建闭包的情况下,因为每次创建闭包都会捕获当前的作用域;

五、一个有趣的实例(定时器与闭包)

for(var i = 1; i <= 5; i++){
    setTimeout(()=>{
        console.log(i);
    }, 1000)
}

执行上述代码,我们的预期结果应该是:1,2,3,4,5(每隔一秒执行一次定时器内的回调);

实际上是输出了5个6,为什么?

因为JavaScript是单线程;代码中的for循环是同步任务,会被放入执行栈中执行;而setTimeout是异步任务,会被放到任务队列等待同步执行完毕,再执行;

当setTimeout定时器执行的时候,同步任务for循环(创建了5个定时器,在任务队列依次等待)已经执行完毕了,i 的值已经加到了6,所以就会打印出5个6了;

怎么才能输出预期结果?

使用闭包来存储定时器被创建时的 i 值;

for (var i = 1; i <= 5; i++) {
    (function (i) {
        setTimeout(() => {
            console.log(i);
        }, 1000);
    })(i)
}

还有一个方式是将var改成let,也能实现,为什么?

将for循环中的var关键字改成let关键字;

for (let i = 1; i <= 5; i++) {
    setTimeout(() => {
        console.log(i);
    }, 1000);
}

=================================================================

先记录到这里吧~!关于闭包的更深入理解,还在学习中;

希望走过路过的大佬们多多指点呀~~~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

OpenCV对图片中的水果进行识别计算其面积长度等

本项目所用到的技术有&#xff1a; OpenCV Python的一些库&#xff1a;sys,openpyxl,numpy,PyQt5,PIL 本文可以做一些课程设计的项目 本文为作者原创&#xff0c;转载请注明出处&#xff0c;如果需要完整的代码&#xff0c;可以关注我私信 上面是用到的样例图片&#xff0c;一张…

如何在OpenFOAM的案例文件夹中确定数据的点和面,确定点和网格之间的关系,从而用于深度学习预测和构建模型呢(分析数据格式及其含义)

在OpenFOAM中&#xff0c;点&#xff08;points&#xff09;和面&#xff08;faces&#xff09;的定义是通过不同的文件来进行的。在案例一级目录下面的constant/polyMesh目录下&#xff0c;会有points, faces, owner, neighbour等文件&#xff0c;来描述网格的几何和拓扑结构。…

最新深度技术Win7精简版系统:免费下载!

在Win7电脑操作中&#xff0c;用户想要给电脑安装上深度技术Win7精简版系统&#xff0c;但不知道去哪里才能找到该系统版本&#xff1f;接下来系统之家小编给大家带来了深度技术Win7系统精简版本的下载地址&#xff0c;方便大家点击下载安装。系统安装步骤已简化&#xff0c;新…

数据解读加密世界的重新定位:全球流动性困境下的转型之痛

全球每一天将会产生50亿次以上的搜索&#xff0c;每一天有491EB的数据出现在我们的生活中&#xff0c;如果这些数据量全部以Email的形式展现&#xff0c;那相当于1个人1天需要处理36亿封电子邮件。随着区块链技术的不断发展&#xff0c;从链下到链上&#xff0c;链上数据构建的…

SVM - 径向基函数核 Radial Basis Function Kernel,简称RBF核或者高斯核

SVM - 径向基函数核 Radial Basis Function Kernel&#xff0c;简称RBF核或者高斯核 flyfish 径向基函数核&#xff08;Radial Basis Function Kernel&#xff0c;简称RBF核&#xff09;&#xff0c;也称为高斯核&#xff0c;是一种常用的核函数&#xff0c;用于支持向量机&a…

百度文心4.0 Turbo开放,领跑国内AI大模型赛道!

百度文心4.0 Turbo开放&#xff0c;领跑国内AI大模型赛道&#xff01; 前言 文心一言大模型 就在7月5日&#xff0c;在2024世界人工智能大会 (WAIC) 上&#xff0c;百度副总裁谢广军宣布文心大模型4.0 Turbo正式向企业客户全面开放&#xff01;这一举动直接引发了业界的关注。那…

国产PLC能否使用无线通讯终端来实现无线通讯?让我们一探究竟

在工业生产车间内&#xff0c;PLC被广泛应用于自动化控制的各个环节。随着智能化工厂的建设&#xff0c;许多PLC仍处于信息孤岛状态&#xff0c;现要将厂区内分散的PLC都建立通讯&#xff0c;如果重新布线工厂量大且不美观&#xff0c;此时就需要用到工业PLC无线通讯设备来解决…

Knife4j的介绍与使用

目录 一、简单介绍1.1 简介1.2 主要特点和功能&#xff1a; 二、使用步骤&#xff1a;2.1 添加依赖&#xff1a;2.2 yml数据源配置2.3 创建knife4j配置类2.4 注解的作用 最后 一、简单介绍 1.1 简介 Knife4j 是一款基于Swagger的开源文档管理工具&#xff0c;主要用于生成和管…

【Android应用】生成证书和打包

安卓生成证书和打包 &#x1f4d6;1. 生成自有证书&#x1f4d6;2. 安卓打包✅步骤一&#xff1a;导入签名文件✅步骤二&#xff1a;设置打包版本✅步骤三&#xff1a;生成签名包或APK &#x1f4d6;1. 生成自有证书 地址&#xff1a;https://www.yunedit.com/createcert 说明…

Cesium版本升级webgl问题,glsl代码关键字修改

简介 Cesium 从1.102.0 开始&#xff0c;Cesium 默认使用 WebGL2 上下文。一些webgl特效代码在webgl1中支持&#xff0c;但是在版本升级后&#xff0c;运行会报各种glsl代码错误。现在有两种解决方案。详细办法描述如下所示。 1、修改配置使用WebGL1 地球初始化配置如下&…

【计算机毕业设计】基于Springboot的IT技术交流和分享平台【源码+lw+部署文档】

包含论文源码的压缩包较大&#xff0c;请私信或者加我的绿色小软件获取 免责声明&#xff1a;资料部分来源于合法的互联网渠道收集和整理&#xff0c;部分自己学习积累成果&#xff0c;供大家学习参考与交流。收取的费用仅用于收集和整理资料耗费时间的酬劳。 本人尊重原创作者…

20240709每日后端--------最优解决Invalid bound statement (not found)

目标 最优解决Invalid bound statement (not found) 步骤 1、打包 2、查看target下是否成双成对出现 3、核对无误后&#xff0c;即可解决问题。

开发者必看:MySQL主从复制与Laravel读写分离的完美搭配

介绍 主从同步配置的主要性不用多说&#xff0c;本文将详细介绍了如何在MySQL数据库中设置主从复制&#xff0c;以及如何在Laravel框架中实现数据库的读写分离。 通过一系列的步骤&#xff0c;包括修改MySQL配置、创建同步账户、获取二进制日志文件名和位置、导出主服务器数据…

原创作品—数据可视化大屏

设计数据可视化大屏时&#xff0c;用户体验方面需注重以下几点&#xff1a;首先&#xff0c;确保大屏信息层次分明&#xff0c;主要数据突出显示&#xff0c;次要信息适当弱化&#xff0c;帮助用户快速捕捉关键信息。其次&#xff0c;设计应直观易懂&#xff0c;避免复杂难懂的…

南方CASS:测绘工作者的得力助手

初识南方CASS 南方CASS&#xff0c;这款软件在我的工作和生活中扮演了不可或缺的角色。作为一名长期的使用者&#xff0c;我深感它的功能之强大与便捷。今天&#xff0c;我愿以季羡林先生的口吻&#xff0c;带大家走进南方CASS的世界&#xff0c;分享我与它的点滴故事。 功能…

GTK是如何加密WLAN组播和广播数据的?

1. References WLAN 4-Way Handshake如何生成GTK&#xff1f;_tk bigtk gtk igtk-CSDN博客 2. 概述 在Wi-Fi网络中&#xff0c;单播、组播和广播帧的加密算法是由AP决定的。其中组播帧和广播帧的加密使用GTK密钥&#xff0c;其PTK的密钥结构如下图所示&#xff1a; GTK的组成…

为何要进行算法备案?保障数据安全不容小觑!

在数字经济高速发展的今天&#xff0c;数据已成为企业运营的核心资产&#xff0c;而算法则是处理和利用这些数据的关键技术。随着大数据时代的来临&#xff0c;数据安全问题日益凸显&#xff0c;同时&#xff0c;国家对于数据安全和算法应用的监管也日趋严格。在这样的背景下&a…

第一个基于FISCOBCOS的前后端项目(发行转账)

本文旨在介绍一个简单的基于fiscobcos的前后端网站应用。Springbootjs前后端不分离。 所使用到的合约也是一个最基本的。首先您需要知道的是完整项目分为三部分&#xff0c;1是区块链平台webase搭建&#xff08;此项目使用节点前置webase-front即可&#xff09;&#xff0c;2是…

YOLOv8改进 | 注意力机制 | 对密集和小目标友好的EVAblock 【原理 + 完整代码】

秋招面试专栏推荐 &#xff1a;深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 &#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 专栏目录 &#xff1a;《YOLOv8改进有效…

Object方法重写

这篇文章大家随意看看就好&#xff0c;只是一点理解的东西&#xff0c;当然你也可以认真调查。 我们需要知道Obecj是java中的一个类&#xff0c;是所有类的父类&#xff0c;即超类。对&#xff0c;超级赛亚人的那个超。 我们需要关注其中的equals、tostring这两个方法。 例如&…