浅析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 相关文章推荐
利用js 进行输入框自动匹配字符的小例子
Jun 29 Javascript
js实现数组去重、判断数组以及对象中的内容是否相同
Nov 29 Javascript
JS实现点击链接取消跳转效果的方法
Jan 24 Javascript
jqeury-easyui-layout问题解决方法
Mar 24 Javascript
jQuery实现的一个自定义Placeholder属性插件
Aug 11 Javascript
js+CSS实现弹出居中背景半透明div层的方法
Feb 26 Javascript
DEDECMS如何为文章添加HOT NEW标志图片
Aug 14 Javascript
JS实现黑色风格的网页TAB选项卡效果代码
Oct 09 Javascript
AngularJS自动表单验证
Feb 01 Javascript
JS实现复制功能
Mar 01 Javascript
解决layui中onchange失效以及form动态渲染失效的问题
Sep 27 Javascript
使用原生javascript开发计算器实例代码
Feb 21 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
PHP安全配置
2006/10/09 PHP
浅析PHP原理之变量(Variables inside PHP)
2013/08/09 PHP
php while循环得到循环次数
2013/10/26 PHP
PHP递归复制、移动目录的自定义函数分享
2014/11/18 PHP
php基于单例模式封装mysql类完整实例
2016/10/18 PHP
[原创]PHP实现字节数Byte转换为KB、MB、GB、TB的方法
2017/08/31 PHP
JavaScript使用prototype定义对象类型(转)[
2006/12/22 Javascript
比较搞笑的js陷阱题
2010/02/07 Javascript
基于JQuery的Select选择框的华丽变身
2011/08/23 Javascript
js中判断Object、Array、Function等引用类型对象是否相等
2012/08/29 Javascript
caller和callee的区别介绍及演示结果
2013/03/10 Javascript
addEventListener()第三个参数useCapture (Boolean)详细解析
2013/11/07 Javascript
JavaScript拆分字符串时产生空字符的解决方案
2014/09/26 Javascript
JS实现超简洁网页title标题跑动闪烁提示效果代码
2015/10/23 Javascript
基于jquery实现图片上传本地预览功能
2016/01/08 Javascript
Js与Jq获取浏览器和对象值的方法
2016/03/18 Javascript
require.js配合插件text.js实现最简单的单页应用程序
2016/07/12 Javascript
详解基于iview-ui的导航栏路径(面包屑)配置
2019/02/22 Javascript
JS实现数组删除指定元素功能示例
2019/06/05 Javascript
微信小程序淘宝首页双排图片布局排版代码(推荐)
2020/10/29 Javascript
[46:44]VG vs TNC Supermajor小组赛B组败者组决赛 BO3 第一场 6.2
2018/06/03 DOTA
Python学习笔记之常用函数及说明
2014/05/23 Python
利用Python获取赶集网招聘信息前篇
2016/04/18 Python
python如何在终端里面显示一张图片
2016/08/17 Python
python分析作业提交情况
2017/11/22 Python
python多维数组切片方法
2018/04/13 Python
浅谈python标准库--functools.partial
2019/03/13 Python
pytorch 利用lstm做mnist手写数字识别分类的实例
2020/01/10 Python
tensorflow实现在函数中用tf.Print输出中间值
2020/01/21 Python
Docker如何部署Python项目的实现详解
2020/10/26 Python
Manjaro、pip、conda更换国内源的方法
2020/11/17 Python
2014年乡镇领导个人整改措施
2014/09/19 职场文书
2014年学生会生活部工作总结
2014/11/07 职场文书
三峡导游词
2015/01/31 职场文书
撤诉书怎么写
2015/05/19 职场文书
hive数据仓库新增字段方法
2022/06/25 数据库