浅析JavaScript中的变量提升


Posted in Javascript onJune 01, 2022

前言:

JavaScript中奇怪的一点是你可以在变量和函数声明之前使用它们。就好像是变量声明和函数声明被提升了代码的顶部一样。

sayHi() // Hi there!

function sayHi() {
    console.log('Hi there!')
}
name = 'John Doe'
console.log(name)   // John Doe
var name

然而JavaScript并不会移动你的代码,所以JavaScript中“变量提升”并不是真正意义上的“提升”。

JavaScript是单线程语言,所以执行肯定是按顺序执行。但是并不是逐行的分析和执行,而是一段一段地分析执行,会先进行编译阶段然后才是执行阶段。

在编译阶段阶段,代码真正执行前的几毫秒,会检测到所有的变量和函数声明,所有这些函数和变量声明都被添加到名为Lexical Environment的JavaScript数据结构内的内存中。所以这些变量和函数能在它们真正被声明之前使用。

函数提升

sayHi() // Hi there!

function sayHi() {
    console.log('Hi there!')
}

因为函数声明在编译阶段会被添加到词法环境(Lexical Environment)中,当JavaScript引擎遇到sayHi()函数时,它会从词法环境中找到这个函数并执行它。

lexicalEnvironment = {
  sayHi: < func >
}

var变量提升

console.log(name)   // 'undefined'
var name = 'John Doe'
console.log(name)   // John Doe

上面的代码实际上分为两个部分:

  • var name表示声明变量name
  • = 'John Doe'表示的是为变量name赋值为'John Doe'。
var name    // 声明变量
name = 'John Doe' // 赋值操作

只有声明操作var name会被提升,而赋值这个操作并不会被提升,但是为什么变量name的值会是undefined呢?

原因是当JavaScript在编译阶段会找到var关键字声明的变量会添加到词法环境中,并初始化一个值undefined,在之后执行代码到赋值语句时,会把值赋值到这个变量。

// 编译阶段
lexicalEnvironment = {
  name: undefined
}
// 执行阶段
lexicalEnvironment = {
  name: 'John Doe'
}

所以函数表达式也不会被“提升”。helloWorld是一个默认值是undefined的变量,而不是一个function

helloWorld();  // TypeError: helloWorld is not a function
var helloWorld = function(){
  console.log('Hello World!');
}

let & const提升

console.log(a)  // ReferenceError: a is not defined
let a = 3

为什么会报一个ReferenceError错误,难道letconst声明的变量没有被“提升”吗?

事实上所有的声明(function, var, let, const, class)都会被“提升”。但是只有使用var关键字声明的变量才会被初始化undefined值,而letconst声明的变量则不会被初始化值。

只有在执行阶段JavaScript引擎在遇到他们的词法绑定(赋值)时,他们才会被初始化。这意味着在JavaScript引擎在声明变量之前,无法访问该变量。这就是我们所说的Temporal Dead Zone,即变量创建和初始化之间的时间跨度,它们无法访问。

如果JavaScript引擎在letconst变量被声明的地方还找不到值的话,就会被赋值为undefined或者返回一个错误(const的情况下)。

举例:

let a
console.log(a)  // undefined
a = 5

在编译阶段,JavaScript引擎遇到变量a并将它存到词法环境中,但因为使用let关键字声明的,JavaScript引擎并不会为它初始化值,所以在编译阶段,此刻的词法环境像这样:

lexicalEnvironment = {
  a: <uninitialized>
}

如果我们要在变量声明之前使用变量,JavaScript引擎会从词法环境中获取变量的值,但是变量此时还是uninitialized状态,所以会返回一个错误ReferenceError

在执行阶段,当JavaScript引擎执行到变量被声明的时候,如果声明了变量并赋值,会更新词法环境中的值,如果只是声明了变量没有被赋值,那么JavaScript引擎会给变量赋值为undefined

tips: 我们可以在letconst声明之前使用他们,只要代码不是在变量声明之前执行:

function foo() {
    console.log(name)
}
let name = 'John Doe'
foo()   // 'John Doe'

Class提升

letconst一样,class在JavaScript中也是会被“提升”的,在被真正赋值之前都不会被初始化值, 同样受Temporal Dead Zone的影响。

let peter = new Person('Peter', 25) // ReferenceError: Person is not defined
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}
let John = new Person('John', 25); 
console.log(John) // Person { name: 'John', age: 25 }

到此这篇关于浅析JavaScript中的变量提升的文章就介绍到这了,更多相关JS变量提升内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!


Tags in this post...

Javascript 相关文章推荐
JSON格式化输出
Nov 10 Javascript
js实现按钮颜色渐变动画效果
Aug 20 Javascript
jfinal与bootstrap的登录跳转实战演习
Sep 22 Javascript
js简单网速测试方法完整实例
Dec 15 Javascript
JavaScript中的ParseInt(&quot;08&quot;)和“09”返回0的原因分析及解决办法
May 19 Javascript
逻辑表达式中与或非的用法详解
Jun 06 Javascript
jQuery实现判断控件是否显示的方法
Jan 11 Javascript
以v-model与promise两种方式实现vue弹窗组件
May 21 Javascript
D3.js实现拓扑图的示例代码
Jun 30 Javascript
Vue-cli配置打包文件本地使用的教程图解
Aug 02 Javascript
详解如何用typescript开发koa2的二三事
Nov 13 Javascript
微信小程序scroll-view锚点链接滚动跳转功能
Dec 12 Javascript
vue ant design 封装弹窗表单的使用
Jun 01 #Vue.js
让JavaScript代码更加精简的方法技巧
Jun 01 #Javascript
cypress测试本地web应用
Jun 01 #Javascript
vue实现登陆页面开发实践
May 30 #Vue.js
Echarts如何重新渲染实例详解
May 30 #Javascript
vue router 动态路由清除方式
May 25 #Vue.js
vue如何清除浏览器历史栈
May 25 #Vue.js
You might like
曾在DC漫画界反派角色扮演的演员,谁才是你心目中的小丑之王?
2020/04/09 欧美动漫
PHP初学者头疼问题总结
2006/10/09 PHP
php中文字母数字验证码实现代码
2008/04/25 PHP
微信营销平台系统?刮刮乐的开发
2014/06/10 PHP
PHP 验证身份证是否合法的函数
2017/02/09 PHP
Dojo之路:如何利用Dojo实现Drag and Drop效果
2007/04/10 Javascript
jquery自动完成插件(autocomplete)应用之PHP版
2009/12/15 Javascript
JS getStyle获取最终样式函数代码
2010/04/01 Javascript
jQuery不间断滚动效果(模拟百度新闻支持文字/图片/垂直滚动)
2013/02/05 Javascript
js验证是否为数字的总结
2013/04/14 Javascript
使用jquery选择器如何获取父级元素、同级元素、子元素
2014/05/14 Javascript
Jquery插件分享之气泡形提示控件grumble.js
2014/05/20 Javascript
node.js中的console.timeEnd方法使用说明
2014/12/09 Javascript
JavaScript使用简单正则表达式的数据验证功能示例
2017/01/13 Javascript
Javascript下拉刷新的简单实现
2017/02/14 Javascript
Angular使用 ng-img-max 调整浏览器中的图片的示例代码
2017/08/17 Javascript
微信小程序中上传图片并进行压缩的实现代码
2018/08/28 Javascript
vue-router 起步步骤详解
2019/03/26 Javascript
利用Electron简单撸一个Markdown编辑器的方法
2019/06/10 Javascript
vue中使用 pako.js 解密 gzip加密字符串的方法
2019/06/10 Javascript
基于Node.js的大文件分片上传示例
2019/06/19 Javascript
vue+iview使用树形控件的具体使用
2020/11/02 Javascript
[02:20]DOTA2亚洲邀请赛 IG战队出场宣传片
2015/02/07 DOTA
解决PyCharm控制台输出乱码的问题
2019/01/16 Python
简单瞅瞅Python vars()内置函数的实现
2019/09/27 Python
Python实现发票自动校核微信机器人的方法
2020/05/22 Python
scrapy redis配置文件setting参数详解
2020/11/18 Python
HTML5使用Audio标签实现歌词同步的效果
2016/03/17 HTML / CSS
美国网上订购鲜花:FTD
2016/09/23 全球购物
Cole Haan官方网站:美国时尚潮流品牌
2017/12/06 全球购物
母亲节感恩活动记录
2014/03/16 职场文书
公司董事长助理工作职责
2014/07/12 职场文书
教师优秀党员事迹材料
2014/08/14 职场文书
导游词之西湖雷峰塔
2019/09/18 职场文书
导游词之四川熊猫基地
2020/01/13 职场文书
微信小程序实现拍照和相册选取图片
2021/05/09 Javascript