news 2026/7/28 20:03:42

详解变量提升和闭包的应用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
详解变量提升和闭包的应用

文章目录

    • 堆栈内存
    • 变量提升
    • 带var和不带var的区别
    • 作用域链的拓展
    • 闭包
      • 什么是闭包
      • 闭包的应用

堆栈内存

栈内存:作用域

  • 提供一个供js代码自上而下执行的环境(代码都是在栈内存中执行)
  • 存储基本数据类型
  • 释放:当栈内存被销毁,存储的那些基本值也跟着被销毁了(一般函数执行完成所形成的私有作用域会自动释放,除非某些内容被栈内存以外的变量占用了此时栈内存不能释放)全局栈内存只有在页面关闭的时候被释放

堆内存:引用值对应的空间

  • 存储引用类型值(对象:键值对 函数:代码字符串)
  • 当前堆内存释放销毁,那么这个引用值彻底没了
  • 释放:当对内存没有被任何的变量或其他东西所占用,浏览器会在空闲的时候自主进行内存回收,把所有不被占用堆的内存销毁掉。比如我让ary1=null,null是空对象指针,让原始变量谁都不指,原有被占用的对内存就没有被东西占用了,浏览器在空闲时候就把该堆内存销毁了。

举例:
var a=10经历三个过程,声明变量a,没有赋值默认是undefined;在当前作用域开辟一个位置存储12(栈);让变量a和12关联在一起(定义:赋值)
var a=[1,2],声明变量a;开辟一个新的内存空间存储[1,2](堆);让变量a和该对内存地址关联起来
如果是函数的话,会开辟一个新的内存空间存储代码字符串,执行的时候开辟一个私有作用域(栈)把之前创建函数时存储的字符串执行,执行完毕,该栈内存销毁。

变量提升

js是先预编译再执行,预编译的过程就是扫一遍把变量绑定作用域。所以才有变量提升。
带var的只声明未定义,带function的把声明定义都完成了

console.log(a)console.log(sum)vara=1functionsum(){console.log('hello world')}

执行结果是

【注意】
私有作用域,先形参赋值再变量提升再代码执行

带var和不带var的区别

在全局作用域下声明一个变量,也相当于给window对象设置了一个属性,属性的值就是属性值(私有作用域声明的私有变量和window没关系)

console.log(a)//undefined 变量提升不报错conosle.log(window.a)//undefined 给window设置一个对象console.log('a'inwindow)//true window存在a属性vara=12console.log(a)//12conosle.log(window.a)//12

如果不带var,看以下代码

a=12conosle.log(a)//12console.log(window.a)//12
console.log(a)//报错 不执行a=12conosle.log(a)console.log(window.a)
console.log(window.a)//undefinedconsole.log('a'inwindow)//falsea=12//=>window.a = 12conosle.log(a)//12console.log(window.a)//12

【总结】
在全局作用域

  • 加var是全局变量,由于window对象和全局有映射关系,也就设置了window的属性
  • 不加var的本质是window下的属性,自然没有变量提升

【拓展】
在私有作用域中

  • 带var的在私有作用域有变量提升,都声明为死于变量,和外界没有任何关系
  • 不带var不是私有变量,它会向它的上级作用域查找,看是否为上级变量,不是则继续向上查找,一直查找到window位置,这种查找机制叫做私有作用域链

作用域链的拓展

functionfn(){//变量提升:无b=13console.log('b'inwindow)//trueconsole.log(b)}fn();//13console.log(b)//13

在作用域链查找过程中,如果找到win也没有这个变量,相当于给win设置了一个属性b(window.b=13)

闭包

什么是闭包

  • ·存在没被释放的内存
  • 当前作用域使用其他作用域的变量
    比如
functionb(){vara=1returnfunctionc(){console.log(a)}}vare=b()e()//输出1

这就是个闭包,b执行完了栈内存也没有被释放,里面的a被b栈内存以外的变量引用。再看e,执行的时候器作用域是全局作用域,但使用到了b的私有作用域下的变量a

闭包的应用

  1. 为了保证js 的性能(堆栈内存的性能优化),应该尽可能减少闭包的使用(不被销毁的堆栈内存是耗性能的)
  2. 闭包具有保护作用,保护私有变量不受外界干扰

在项目中,尤其是团队写作开发的时候,应尽可能减少全局变量的使用,以防止相互之间的冲突,那么此时哦我们可以把自己的一部分内容封装到一个闭包里,让全局变量变为私有变量。基于return把需要供外面使用的方法暴露出去。

varZepto=(function(){//...return{xxx:function(){}}})

或者,当想改变一个对象的值,比如user,之前用user.name,但这个方法不够好,当程序过于复杂,会一不小心改动一些东西,变化的轨迹就非常难以追踪。虽然有效但非常危险。所以想把修改过程管控起来,就用到了闭包。
可以定义一个function user(name)。在里面var一个age,sex。这时候返回的不是一个确切的数值,是一个对象,对象里包含不同的方法,比如getName,setName。方法就是定义在对象里面的函数。
当做的项目内部数据流足够复杂,那我们此处必须得找个办法来归纳,不能让对象里面的值在里面被随便修改,必须得找一个像一个接口一样的东西,必须得按我的方法才能修改和设置某个值,这样严谨很多。闭包可以通过走子函数来获取母函数的函数作用域,然后去存取里面的值。这就是闭包的好处和特点。

3. 闭包具有保存作用,形成不销毁的栈内存,把一些值保存下来,方便后面的调取使用
可以基于闭包解决用var for循环给多个按钮绑定事件,无论点击第几个i都是最后那个。(这个问题可以直接直接用let解决,此处说用闭包解决的办法)
假如有五个按钮

for(vari=0;i<buttonList.length;i++){buttonList[i].onclick=function(){console.log(i)}}//此时无论点击第几个按钮打印的都是4,因为js在es6之前没有块级作用域。当我们点击的时候,外层循环已经结束(可以点击的时候页面已经加载完成,页面加载完成预示着js代码都已经执行完成,也就是循环也都执行完成,外层循环结束已经让全局下的i为4)

用闭包解决如下:

for(vari=0;i<buttonList.length;i++){buttonList[i].onclick=(function(){//让自执行函数执行,把执行的返回值(return)赋值给onclick,此时onclick绑定的是返回的小函数,点击的时候执行的是小函数,自执行函数在给事件赋值的时候已经执行了returnfunction(){console.log(i)//上级作用域,自执行函数形成的作用域}})()}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/28 19:59:04

外文人工翻译平台长测:小语种服务对比

一、小语种学术翻译&#xff0c;为何成为投稿“拦路虎”&#xff1f; 打开审稿人的反馈邮件&#xff0c;一行刺眼的“The abstract lacks fluency, please have it revised by a native speaker”让辛苦数月的研究成果卡在语言关。对于需要投递法语、德语、日语等小语种期刊的…

作者头像 李华
网站建设 2026/7/28 19:57:54

暗黑破坏神4闪退问题全解析:从驱动更新到硬件诊断的完整解决方案

最近《暗黑破坏神4》新赛季开启&#xff0c;不少玩家兴冲冲地登录游戏&#xff0c;结果刚看到角色选择界面就闪退&#xff0c;连游戏都进不去。这种"一登陆就闪退"的问题确实让人头疼&#xff0c;特别是当你期待已久想要体验新内容时。从技术角度看&#xff0c;这类问…

作者头像 李华
网站建设 2026/7/28 19:54:13

vue中央事件总线bus

全局 1、新建 eventBus.js 文件import Vue from vue export default new Vue()2、引用 (哪里要用就import eventBus.js 文件&#xff09;$on : 监听 $emit &#xff1a;触发 $off : 释放 ( 监听后一定要在beforeDestroy中释放&#xff09;

作者头像 李华
网站建设 2026/7/28 19:49:59

C++ 中到底是应该include .h文件还是应该include .cpp文件

在阅读一个较大的解决方案中&#xff0c;对于其他文件夹下的.h和.cpp文件&#xff0c;有时候#include“XXX.h”文件&#xff0c;有时候是#include“XXX.cpp”文件&#xff0c;而且二者还不能更换。下面就好好分析一下他们二者的区别。 文章目录测试测试&#xff1a;XXX.h和XXX.…

作者头像 李华
网站建设 2026/7/28 19:49:41

暗黑破坏神2存档编辑器完全指南:5分钟学会角色与装备修改

暗黑破坏神2存档编辑器完全指南&#xff1a;5分钟学会角色与装备修改 【免费下载链接】d2s-editor 项目地址: https://gitcode.com/gh_mirrors/d2/d2s-editor 还在为暗黑破坏神2存档损坏而烦恼&#xff1f;想测试新build却不想重新练级&#xff1f;暗黑破坏神2存档编辑…

作者头像 李华