深入理解Javascript闭包概念

深入理解JavaScript闭包:概念、原理与实战应用 网上关于闭包的标准定义通常是:“一个函数能够访问其外部作用域中的变量”。听起来没错,但太抽象了,容易让人误以为所有函数都是闭包。举个反例:这里的inner虽然嵌套在outer内部,但它根本没有引用任何外部变量。所以严格来说,它没有形成闭包。✅ 那什么样的函数才算真正的闭包呢?当内部函数捕获并使用了外层函数的作用域变量,并且该内部函数被传递到其定义环境之外执行时,才构成了闭包。也就是说,闭包不是自动产生的语法结构,而是一种由运行时行为触发的状态。 阅读详情

一、变量的作用域

要理解闭包,首先必须理解Javascript特殊的变量作用域。

变量的作用域无非就是两种:全局变量和局部变量。

Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。


Js代码

  var n=999;

  function f1(){
    alert(n);
  }

  f1(); // 999

另一方面,在函数外部自然无法读取函数内的局部变量。

Js代码

  function f1(){
    var n=999;
  }

  alert(n); // error

这里有一个地方需要注意,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!

Js代码

  function f1(){
    n=999;
  }

  f1();

  alert(n); // 999

--------------------------------------------------------------------------------------------------------

二、如何从外部读取局部变量?

出于种种原因,我们有时候需要得到函数内的局部变量。但是,前面已经说过了,正常情况下,这是办不到的,只有通过变通方法才能实现。

那就是在函数的内部,再定义一个函数。

Js代码

  function f1(){

    n=999;

    function f2(){
      alert(n); // 999
    }

  }

在上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1 就是不可见的。这就是Javascript语言特有的“链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!


Js代码

  function f1(){

    n=999;

    function f2(){
      alert(n);
    }

    return f2;

  }

  var result=f1();

  result(); // 999

--------------------------------------------------------------------------------------------------------

三、闭包的概念

上一节代码中的f2函数,就是闭包。

各种专业文献上的“闭包”(closure)定义非常抽象,很难看懂。我的理解是,闭包就是能够读取其他函数内部变量的函数。

由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”。

所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

--------------------------------------------------------------------------------------------------------

四、闭包的用途

闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

怎么来理解这句话呢?请看下面的代码。


Js代码

  function f1(){

    var n=999;

    nAdd=function(){n+=1}

    function f2(){
      alert(n);
    }

    return f2;

  }

  var result=f1();

  result(); // 999

  nAdd();

  result(); // 1000

在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。

为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

这段代码中另一个值得注意的地方,就是“nAdd=function(){n+=1}”这一行,首先在nAdd前面没有使用var关键字,因此 nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。

-------------------------------------------------------------------------------------------------------- 

五、使用闭包的注意点

1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便

改变父函数内部变量的值。

--------------------------------------------------------------------------------------------------------

六、闭包内的微观世界

如果要更加深入的了解闭包以及函数a和嵌套函数b的关系,我们需要引入另外几个概念:函数的执行环境(excution context)、活动对象(call object)、作用域(scope)、作用域链(scope chain)。以函数a从定义到执行的过程为例阐述这几个概念。

  1. 当定义函数a的时候,js解释器会将函数a的作用域链(scope chain)设置为定义a时a所在的“环境”,如果a是一个全局函数,则scope chain中只有window对象。
  2. 当执行函数a的时候,a会进入相应的执行环境(excution context)。
  3. 在创建执行环境的过程中,首先会为a添加一个scope属性,即a的作用域,其值就为第1步中的scope chain。即a.scope=a的作用域链。
  4. 然后执行环境会创建一个活动对象(call object)。活动对象也是一个拥有属性的对象,但它不具有原型而且不能通过JavaScript代码直接访问。创建完活动对象后,把活动对象添加到a的作用域链的最顶端。此时a的作用域链包含了两个对象:a的活动对象和window对象。
  5. 下一步是在活动对象上添加一个arguments属性,它保存着调用函数a时所传递的参数。
  6. 最后把所有函数a的形参和内部的函数b的引用也添加到a的活动对象上。在这一步中,完成了函数b的的定义,因此如同第3步,函数b的作用域链被设置为b所被定义的环境,即a的作用域。

到此,整个函数a从定义到执行的步骤就完成了。此时a返回函数b的引用给c,又函数b的作用域链包含了对函数a的活动对象的引用,也就是说b可以访问到a中定义的所有变量和函数。函数b被c引用,函数b又依赖函数a,因此函数a在返回后不会被GC回收。

当函数b执行的时候亦会像以上步骤一样。因此,执行时b的作用域链包含了3个对象:b的活动对象、a的活动对象和window对象,如下图所示:

如图所示,当在函数b中访问一个变量的时候,搜索顺序是:

  1. 先搜索自身的活动对象,如果存在则返回,如果不存在将继续搜索函数a的活动对象,依次查找,直到找到为止。
  2. 如果函数b存在prototype原型对象,则在查找完自身的活动对象后先查找自身的原型对象,再继续查找。这就是Javascript中的变量查找机制。
  3. 如果整个作用域链上都无法找到,则返回undefined。

小结,本段中提到了两个重要的词语:函数的定义与执行。文中提到函数的作用域是在定义函数时候就已经确定,而不是在执行的时候确定(参看步骤1和3)。用一段代码来说明这个问题:

function f(x) { 
  var g = function () { return x; }
  return g;
}
var h = f(1);
alert(h()); 

这段代码中变量h指向了f中的那个匿名函数(由g返回)。

  • 假设函数h的作用域是在执行alert(h())确定的,那么此时h的作用域链是:h的活动对象->alert的活动对象->window对象。
  • 假设函数h的作用域是在定义时确定的,就是说h指向的那个匿名函数在定义的时候就已经确定了作用域。那么在执行的时候,h的作用域链为:h的活动对象->f的活动对象->window对象。

如果第一种假设成立,那输出值就是undefined;如果第二种假设成立,输出值则为1。

运行结果证明了第2个假设是正确的,说明函数的作用域确实是在定义这个函数的时候就已经确定了



海思Hi3519AV100 sensor移植实战:IMX586驱动调试与硬件验证全流程 本文详细解析了在海思Hi3519AV100平台上移植索尼IMX586传感器的全流程实战。内容涵盖从硬件I2C通信验证、驱动代码移植与修改,到内核配置、时钟设定,以及使用示波器进行硬件信号深度验证和ISP图像调优等关键步骤,为开发者提供了一套清晰、可复现的sensor移植与调试方法。 阅读详情

相关推荐

c++11 无锁队列

/************************************************************************* > File Name: lockfreequeue.cpp > Author: > Mail: > Created Time: Wed 08 Aug 2018 11:38:08 AM CST ********...

qq_35728402的博客 5022

深入理解 JavaScript 闭包:从概念到实践

闭包是 JS 中函数与其定义时作用域的组合,使函数在外部执行时仍能访问内部变量。基于作用域链,可实现模块化、私有变量和缓存,合理使用能写出更优雅安全的代码。

ik_makabaka的博客 1223

OpenMMLab实战指南:从环境搭建到首个检测模型运行

本文是一份详细的OpenMMLab实战指南,重点介绍了如何从零开始搭建环境并运行首个目标检测模型。内容涵盖Miniconda环境配置、PyTorch与CUDA版本匹配、使用MIM工具安装MMCV和MMDetection的核心步骤,并通过RTMDet-tiny模型演示了完整的推理流程,帮助开发者高效入门这一计算机视觉开源算法体系。

weixin_28678151的博客 157

深入理解JavaScript闭包概念

闭包向来给包括JavaScript程序员在内的程序员以神秘,高深的感觉,事实上,闭包的概念在函数式编程语言中算不上是难以理解的知识。如果对作用域,函数为独立的对象这样的基本概念理解较好的话,理解闭包的概念并在实际的编程实践中应用则颇有水到渠成之感。   在DOM的事件处理方面,大多数程序员甚至自己已经在使用闭包了而不自知,在这种情况下,对于浏览器中内嵌的JavaScript引擎的bug可能造成

a1104094910的博客 423

葵花 Himawari 8/9号数据学习记录-01-数据初识

葵花卫星学习记录,第一章:卫星的来源与卫星数据的波段介绍

weixin_44111909的博客 537

光谱预处理最简单实现方法--基于OpenSA光谱分析库

系列文章目录 “光晰本质,谱见不同”,光谱作为物质的指纹,被广泛应用于成分分析中。伴随微型光谱仪/光谱成像仪的发展与普及,基于光谱的分析技术将不只停留于工业和实验室,即将走入生活,实现万物感知,见微知著。本系列文章致力于光谱分析技术的科普和应用。 文章目录系列文章目录前言一、数据来源二、代码解读2.1 读取数据2 .2 划分数据集2 .3 返回训练集和测试集并绘制光谱图片2.4 建立1-D CNN模型2.5 训练1-D CNN模型2.6 测试1-D CNN模型2.7 运行CNN模型,并用查看结果总结

Echo_Code的博客 7067

深入理解JavaScript闭包:从概念到实践

在JavaScript中,闭包是指函数以及其创建时所处的词法环境的组合。简单来说,闭包是由函数以及函数内部能够访问的外部变量组成的包裹(closure)。闭包允许函数访问其外部作用域中的变量,即使函数在其创建的环境之外执行。通过本篇博客,我们详细讲解了JavaScript闭包的概念、工作原理和应用场景。闭包是JavaScript中一个强大的特性,它使得我们能够编写更加灵活、高效和可维护的代码。理解闭包的概念和运作方式,对于提升JavaScript编程能力至关重要。希望本篇博客对您的学习有所帮助。

闪电⚡之王的博客简介 439

zynq的irq_f2p 如何增加或修改中断引脚个数(宽度)

ZYNQ开发过程中,如果使用PL到PS的中断,会产生一个疑问,设置界面显示可以有[91:84]、[68:61]共16个中断引脚,但是并没有设置中断口的宽度,如果使用多个中断引脚,应该怎么办?如果通过concat连接多个引脚,则自动调整为对应的引脚数。例如连接3个中断信号,则自动调整为:IRQ_F2P[2:0]

FPGA艺术家的博客 837

mysql闭包的概念_javascript中闭包概念与用法深入理解

本文实例分析了javascript中闭包概念与用法。分享给大家供大家参考,具体如下:1.问题的引出,什么时候会遇到闭包?首先因为JS是没有块状作用域的,但是有函数作用域即函数作为了局部变量之间的界限,不同函数内的局部变量具有独立性,因为JS没有块状作用域,笔者初学JS时,在事件的监听时,因为不理解JS中局部变量的作用域,犯过不少错误!(1)JS中的变量作用域for(var i=0;i<9;i...

weixin_35973521的博客 143

KingSCADA实现按钮点击效果

哈喽,你好啊,我是雷工!在做SCADA项目的时候,按钮是不可缺少的功能,但软件自带的按钮太丑,已经无法满足现如今客户对界面美观度的要求。这时候就需要UI小姐姐设计美观大气的SCADA界面,但UI设计的按钮切图放到程序中只是图片素材,当添加了按钮功能时,虽然能实现按钮功能,但没有任何点击效果;当遇到有需要连锁条件才能点击的按钮时,操作员无法区分是有连锁点击了没反应,还是系统界面卡死了导致的没点击成功,这时候有点击效果就能很好的区分这种问题。本篇介绍一种实现按钮点击效果的方法,以下为操作过程。

做有价值的事,积累有价值的笔记! 2765

先进封装类别分析与典型案例

当制程微缩逼近物理极限、单颗大芯片(reticle limit)面积受限于光罩尺寸,且逻辑、存储、射频、传感等功能无法用同一套工艺最经济地制造时,性能增益的来源从 "晶体管密度" 转向 "互连密度"。先进封装的本质,是在芯片外部用更短、更密、更宽的路径把多个裸 die(die)互连起来,换取更高带宽、更低延迟、更小体积和异构集成能力。行业通常按划分为 2D、2.5D、3D、扇出、SiP 五大谱系,Chiplet/UCIe 则是贯穿其上的设计方法论,玻璃基板、共封装光学(CPO)是正在成型的新分支。

0 33

转:深入理解JavaScript闭包概念

闭包向来给包括JavaScript程序员在内的程序员以神秘,高深的感觉,事实上,闭包的概念在函数式编程语言中算不上是难以理解的知识。如果对作用域,函数为独立的对象这样的基本概念理解较好的话,理解闭包的概念并在实际的编程实践中应用则颇有水到渠成之感。在DOM的事件处理方面,大多数程序员甚至自己已经在使用闭包了而不自知,在这种情况下,对于浏览器中内嵌的JavaScript引擎的bug可能造...

weixin_30593443的博客 127

(五)深入理解Bluez协议栈之“GATT Server Profile”

上一篇介绍Bluez协议栈的了GAP Profile的实现,本章节我们继续介绍GATT Profile的实现,参考的程序是Peripheral。通过阅读它的实现代码可以发现,它广播部分是通过mgmt来实现的,然后在初始化完成之后,创建了一个L2CAP类型的socket,来负责实现GATT Server的逻辑。

1142

javascript中重要概念-闭包-深入理解

在上次的分享中javascript--函数参数与闭包--详解,对闭包的解释不够深入。本人经过一段时间的学习,对闭包的概念又有了新的理解。于是便把学习的过程整理成文章,一是为了加深自己闭包的理解,二是给读者提供学习的途径,避免走弯路。   在javascript--函数参数与闭包--详解这篇文章中,我详细介绍了闭包的概念。以下的分享对闭包的基本概念只会稍稍带过。如果对闭包的概念不熟悉的同学,请移...

weixin_33901926的博客 101

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

深入理解JavaScript闭包从基础概念到实际应用场景

闭包(Closure)是 JavaScript 中一种特殊的函数特性,指内层函数能够访问其外部函数作用域中的变量,即使外部函数已经执行结束。当函数内部引用外部变量时,这些变量会被保留在内存中,不会被垃圾回收机制清除。此时 `inner` 函数仍能访问已执行结束的 `outer` 函数中的 `count` 变量。- 持久化变量:闭包使得外部函数的变量在函数执行后继续存在。3. 内层函数被外部函数返回或在其他作用域中被调用。1. 函数嵌套(内层函数定义在外部函数内部)// 闭包保持对 count 的引用。

KNFAlAjL的博客 382

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

javascript教程_开课吧前端教程:如何深入理解Javascript闭包的概念?

想必身为前端开发,大家对“闭包”一词并不陌生,但能真正理解闭包概念,并能熟练使用闭包却是成为了判断前端技术水平的一个硬性标准。很多刚入行的前端,就因为对闭包概念理解不是很透彻,导致大家在面试阶段屡屡碰壁,惨遭pass。那今天我带大家深入理解下关于闭包的概念。开课吧Web前端教程,开课吧Web前端基础,Javascript闭包先看下下列代码:var Closure = function() ...

weixin_39758953的博客 91

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

javascript深入理解js闭包

javascript深入理解js闭包 闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。 一、变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域。 变量的作用域无非就是两种:全局变量和局部变量。 Javascript语言的特殊之处,就在于函数内部可以直接读取全局变

憧憬的专栏 552

深入理解js闭包

闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。 一、变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域。 变量的作用域无非就是两种:全局变量和局部变量。 Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。 Js代码   var n=999;   func

bestlxm的专栏 723

深入理解JavaScript核心概念:执行上下文与闭包

JavaScript作为一门动态脚本语言,其执行上下文和作用域链是理解代码运行机制的核心概念。执行上下文包含变量对象、作用域链和this绑定,决定了代码执行时的变量访问规则。闭包作为JavaScript的特色功能,允许函数访问其词法作用域外的变量,但同时也带来了内存管理的挑战。在实际开发中,合理运用这些概念能显著提升代码质量,特别是在模块化开发、状态管理和性能优化等场景。通过分析执行上下文创建过程和闭包内存管理机制,开发者可以避免常见陷阱,写出更高效、更健壮的JavaScript代码。

weixin_33563728的博客 177

JS闭包简单理解

我对闭包的理解: 1.函数嵌套函数 2.函数内部可以引用外部的参数和变量 3.参数和变量不会被垃圾回收机制回收 我觉得这段说明也很精彩:js里的函数在运行结束之后,所有的过程中产生的变量都会被销毁(销毁原则是无引用)但在js的函数A里定义一个函数B并在外部引用时,这条引用链上的变量都不会销毁(即使A已经运行结束),这个函数B就叫做一个闭包。 function a() { var num...

weixin_46171501的博客 223

一种数据展示方式,UI设计新颖,供大家参考

这款设计主要是用于进行数据分析和对比,譬如,不同省份的一些不同月份的数据对比(图为China Telecom的一些假数据)。 设计取名为LatticeControl。 页面开始,每一个蓝色格子代表一个月份: 右侧的数据显示的是对应省份的营业额以及该省份在总体上的比例。 当鼠标经过某一项时,显示当前月份的信息: 点击某一个格子,当前节的对应项进行高亮显示,同时右侧

前端布道者 3232
上一篇: 浮动的前世今生
下一篇: Git常用命令总结
抱猫睡觉的鱼
博客等级 码龄18年 10粉丝 31原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值