js变量提升原理

本文首发于公众号【一个老码农】

前端的小伙伴大概都知道,js中的var变量存在变量提升,在es6以后随着let变量的出现,变量提升问题得以解决。那么变量提升的原理是什么?es6又是怎么解决变量提升问题的?下面我们来共同探寻答案:

我们首先来了解几个概念,执行上下文、变量环境、词法环境。(本文不涉及闭包、this指向等问题)

目录

执行上下文

当一段js代码被执行时,js引擎会先对其进行编译,并创建执行上下文。执行上下文分为三种:全局执行上下文、函数执行上下文、eval执行上下文

  1. 全局执行上下文
    js执行全局代码时,js引擎会创建一个全局的执行上下文,全局执行上下文在页面的生命周期内只有一份。即每个js文件,只有一个全局上下文。

  2. 函数执行上下文
    当执行一个js函数时,js引擎会创建一个函数执行上下文,当函数执行结束之后,函数的执行上下文会被销毁。一个函数被多次调用,会创建多个执行上下文。

  3. eval执行上下文
    使用eval函数执行一段js代码时,会创建一个eval的执行上下文。

js文件执行时,首先会创建全局执行上下文,并压入调用栈,当调用js函数时,会创建函数执行上下文,并压入调用栈。当函数执行完之后,函数执行上下文便会从栈中移出。如以下代码的执行:

var a = "123"
function func1() {
    var b = "123"
    console.log(b)
    func2()
}
funcgion func2() {
    const c = "456"
    console.log(c)
}
func1()

js变量提升原理

执行上下文中其实还包含了另外两个对象,一个变量环境对象和一个词法环境对象。那么接下来我们来看一下什么是变量环境和词法环境

变量环境

变量环境存在于执行上下文中,其本质是一个对象,变量环境中存储的是此作用域内定义的变量、函数信息等信息。如全局执行上下文中的变量环境存储的是全局的变量和函数信息。函数执行上下文中的变量环境则存放的是函数的参数、局部变量等信息。

其实,js的代码在执行前还有一个编译的过程,在编译过程中,var变量和function函数部分会被js引擎放入到变量环境中,并且变量会被默认设置为undefined。在执行阶段,js引擎会在变量环境中查找声明的变量和函数。这就是我们所说的“变量提升”,这也是为什么函数可以在函数的实现之前调用。

例:

console.log(a)
var a = "123"
function func1() {
    console.log(a)
}
func1()

以上代码的执行顺序是:

  • js引擎先进行编译,并把a变量和func1放入到变量环境中,并把a变量设置为undefined

  • 进入执行阶段,执行第一行代码console.log(a),此时从变量环境中取出a的值为undefined,所以打印结果为undefined

  • 执行第二行代码var a = "123",将变量环境中的a变量赋值为字符串123

  • 执行最后一行代码func1(),js引擎从变量环境中找出对应的func1,并执行里面的代码console.log(a),打印结果为123

所以以上代码输出结果为

undefined
123

虽然在a声明之前打印a变量,但是却并没有报错。

词法环境

ES6之前,js中只支持全局作用域和函数作用域,并不支持块级作用域。ES6之后,js引入了letconst关键字,从而解决了变量提升问题并使js支持了块级作用域。

其实说letconst没有变量提升并不准确,当js代码被编译时,letconst变量代码会被存放在词法环境中。此时letconst变量已经被提升了,但是只是创建被提升,初始化和赋值并没有被提升,如果在赋值之前去读写该变量,便会报错,这就是我们所说的“暂时性死区”。

那实现块级作用域的原理是什么呢?其实在词法环境中,维护了一个作用域栈,栈底是函数的最外层变量(letconst声明的变量),进入一个作用域块后,就会把该作用域中的变量入栈;当作用域中的代码执行完成之后,该作用域的信息就会从栈顶弹出。
我们举个以下例子来说明

例:

function fun()
{
    let a = 1
    {
        let a = 2
        let b = 3
        console.log(a)
        console.log(b)
    }
    console.log(a)
    console.log(b)
}
fun()

如图:
js变量提升原理

  1. fun函数被编译时,外层的a变量首先被创建,并存放至词法环境作用域栈中,此时函数内部的块级作用域中的变量不会被创建。
  2. 当函数执行至作用域块时,let alet b也被创建并入栈存放至栈顶。并将a赋值为2,将赋值为3
  3. 当执行至console.log(a)console.log(b)时,js引擎首先从栈顶找到ab的值并打印出23
  4. 当作用域块执行完成之后,作用域块中的变量信息从栈中弹出。
  5. 接着执行console.log(a)找到的是栈底的a变量,并打印出1。接着执行console.log(b),由于在词法环境和变量环境中都找不到b变量,所以便会报错b is not defined

如果同一个函数中不同作用域存在相同的变量(如上面例子的a),那么变量的查找顺序是怎样的呢?

  1. 首先在词法环境作用域栈的栈顶的变量信息中开始查找
  2. 如果找到该变量,则直接返回该变量在此作用域块中的值,如果没有找到则从栈顶往下依次查找。
  3. 如果从词法环境中的栈顶到栈底都没有找到,则从变量环境中查找。

总结:

讲到这里,我想应该可以回答一下文章开始所提的两个问题:

变量提升的原理是什么?
在js代码编译阶段,var变量和function函数会被js引擎放入到变量环境中,并且var变量会被默认设置为undefined。需要注意的是,var变量只有创建和初始化被提升,赋值并没有被提升;而function的创建、初始化和赋值均会被提升。所以在变量的声明之前,该变量的值是undefined,而函数则可以在声明之前正常调用。

letconst是怎么解决变量提升问题的?
在js代码编译阶段,letconst变量会被js引擎放入到词法环境中。与var一样,letconst变量也被提升了。但只是创建被提升,变量的初始化和赋值并未被提升,如果在赋值之前读写该变量,就会形成暂时性死区。

原文链接:https://juejin.cn/post/7233657672343502904 作者:剑老师_

(0)
我心飞翔我心飞翔
0 0
Dart 學習01——環境搭建
上一篇 2023年5月17日 上午10:58
彻底搞懂var、let、const之间的区别
下一篇 2023年5月17日 上午11:08

相关推荐

  • 一分钟早读系列:推演vue3响应式(二)effect进阶篇 2023年12月23日
  • js 如何判断对象自身为空? 2023年12月18日
  • 如何修改WordPress admin区域的HTTP头 2020年7月22日
  • 开源贡献: antv/dataset瘦身, 从1.78M到1.71KB 2024年3月16日
  • 【Vue面试专题】经典Vue面试题详解! 2024年1月12日
  • [this] 面试官:一句话解释 JavaScript 的 this 2023年5月16日
  • 干货!码住!产品说明书模板必备的5大要素! 2024年2月21日
  • 小册上新|CSS 工程化核心原理与实战 2023年12月22日
  • ES6新特性: Reflect 2024年2月14日
  • Vue响应式原理(6)-调度器实现 2024年1月13日

发表回复

登录后才能评论

近期文章

  • 浏览器中如何获取用户网络状态
  • 完全掌握vue全家桶单元测试 : 6. 深入理解组件测试
  • InqureJS:手搓脚手架必备!让你的命令行交互花里胡哨!
  • 【vite.config.ts】(多环境配置)设置环境文件 .env.local
  • 腾讯、阿里、B站最新面经汇总,有的妥妥的凉经
  • 带你深入Vue.js开发实战,从复杂列表的样式到性能优化
  • 『Django』路由urls
  • go语言如何实现协程的抢占式调度的?
  • 字节面试:如何解决MQ消息积压问题?
  • web server apache tomcat11-16-mbean
  • web server apache tomcat11-17-default-servlet
  • ✅MySQL的脏读、幻读、不可重复度是什么
  • tailwindcss Vue项目CSS开发体验
  • 简单的题,内涵不简单
  • AOP使用案例-记录操作日志
  • 计算机基础系列 —— 虚拟机代码翻译器(2)
  • flea-common使用之本地国际化实现
  • 锁、mvcc、隔离级别、(脏读、不可重复读、幻读)理解
  • 【java】使用表达式处理数据 – Aviator
  • python数据类型-字符串
  • 原生桥接方式:深入了解JavaScript Bridge(JsBridge)
  • 邀请函 | Pulsar Meetup 深圳 2024
  • TTFB时间太长怎么办
  • 超实用!2024年必看的10个导航栏设计
  • 如何使用 Node.js 发送电子邮件全解和相关工具推荐
  • Farmer 滚动动画效果集合 (讲解)
  • WebSocket-Vue3 封装, 支持心跳检测和断线重连
  • 并查集的理论、实现与应用【纯干货】
  • Zustand:简化状态管理的JavaScript库
  • 2024-4-18 群讨论:关于异步HttpClient如何测试验证

知识题库

  • 手把手教你vue项目接入漂亮的验证码
  • 校招前端二面经典react面试题及答案_2023-03-13
  • 字节前端二面react面试题(边面边更)_2023-03-13
  • 滴滴前端一面常考vue面试题(持续更新中)_2023-03-13
  • new Vue的时候到底做了什么_2023-03-13
  • 滴滴前端高频vue面试题(边面边更)_2023-03-13
  • 为啥我要选用Element作为wljslmz.cn子系统的UI框架?
  • vue-cli 是怎么配置babel的?
  • 面试官问我按钮级别权限怎么控制,我说v-if,面试官说再见
  • Vue项目迁移小程序,实操干货分享
  • Element ui: form表单使用
  • vue中加入百度统计
  • 前端开发:如何写一手漂亮的 Vue
  • Vue-travel学习笔记
  • 前端开发者不得不知道的18个常用的网站
  • vue-awesome-swiper的用法&同一页面有多个swiper如何使用
  • 结合 Bootstrap + Vue 组件实现 Laravel 异步分页功能
  • 在 Vue.js 中通过计算属性动态设置属性值
  • Vue 组件注册:基本使用和组件嵌套
  • Vue 组件插槽:父子组件间的内容分发和插槽作用域

深圳SEO优化公司福田网站优化按天扣费松岗网站设计模板民治如何制作网站丹竹头网站seo优化坪山外贸网站建设光明网站定制民治至尊标王爱联设计网站吉祥SEO按效果付费深圳SEO按天扣费惠州百度爱采购观澜如何制作网站同乐关键词排名宝安网站优化南澳优秀网站设计民治网站排名优化坪地外贸网站建设大运模板推广横岗模板网站建设荷坳营销型网站建设双龙百度竞价龙岗网站建设设计大运品牌网站设计吉祥网站搭建西乡网站关键词优化宝安SEO按天扣费丹竹头网站建设设计荷坳网站设计罗湖企业网站建设龙华网页制作歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运

深圳SEO优化公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化