JavaScript中的变量提升本质
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错误,难道let和const声明的变量没有被“提升”吗?
事实上所有的声明(function, var, let, const, class)都会被“提升”。但是只有使用var关键字声明的变量才会被初始化undefined值,而let和const声明的变量则不会被初始化值。
只有在执行阶段JavaScript引擎在遇到他们的词法绑定(赋值)时,他们才会被初始化。这意味着在JavaScript引擎在声明变量之前,无法访问该变量。这就是我们所说的Temporal Dead Zone,即变量创建和初始化之间的时间跨度,它们无法访问。
如果JavaScript引擎在let和const变量被声明的地方还找不到值的话,就会被赋值为undefined或者返回一个错误(const的情况下)。
举例:
let a
console.log(a) // undefined
a = 5
在编译阶段,JavaScript引擎遇到变量a并将它存到词法环境中,但因为使用let关键字声明的,JavaScript引擎并不会为它初始化值,所以在编译阶段,此刻的词法环境像这样:
lexicalEnvironment = {
a: <uninitialized>
}
如果我们要在变量声明之前使用变量,JavaScript引擎会从词法环境中获取变量的值,但是变量此时还是uninitialized状态,所以会返回一个错误ReferenceError。
在执行阶段,当JavaScript引擎执行到变量被声明的时候,如果声明了变量并赋值,会更新词法环境中的值,如果只是声明了变量没有被赋值,那么JavaScript引擎会给变量赋值为undefined。
tips: 我们可以在let和const声明之前使用他们,只要代码不是在变量声明之前执行:
function foo() {
console.log(name)
}
let name = 'John Doe'
foo() // 'John Doe'
Class提升
同let和const一样,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中的变量提升本质的更多相关文章
- 简单谈一谈JavaScript中的变量提升的问题
1,随笔由来 第一天开通博客,用于监督自己学习以及分享一点点浅见,不出意外的话,应该是一周一更或者一周两更. 此博客所写内容主要为前端工作中遇上的一些问题以及常见问题,在此基础上略微发表自己的一点浅 ...
- 谈谈javascript中的变量提升还有函数提升
在很多面试题中,经常会看到关于变量提升,还有函数提升的题目,所以我就写一篇自己理解之后的随笔,方便之后的查阅和复习. 首先举个例子 foo();//undefined function foo(){ ...
- JavaScript中的变量提升和函数提升
在EcmaScript5中只有全局作用域和函数作用域,EcmaScript6增加了块级作用域. 块级作用域(一对花括号{}即为一个块级作用域) 变量提升 console.log(name); //un ...
- JavaScript中的变量提升和严格模式
1.什么是变量提升 所谓的变量提升指的是:函数声明和变量声明总是会被解释器悄悄地被"提升"到方法体(作用域)的最顶部. //先声明后使用 var x; console.log(x) ...
- 在javascript中关于变量与函数的提升
在javascript中关于变量与函数的提升 一.简介 在javascript中声明变量与函数的执行步骤: 1.先预解析变量或函数声明代码,会把用var声明的变量或者函数声明的代码块进行提升操作 2. ...
- JavaScript 中定义变量时有无var声明的区别
关于JavaScript中定义变量时有无var声明的区别 var a=5; //正确 a=5; //正确 在javascript中,以上两种方法都是定义变量的正确方法.微软的Script56.CHM中 ...
- JavaScript学习系列2一JavaScript中的变量作用域
在写这篇文章之前,再次提醒一下 JavaScript 是大小写敏感的语言 // 'test', 'Test', 'TeSt' , 'TEST' 是4个不同的变量名 JavaScript中的变量,最重要 ...
- JavaScript 中的变量命名方法
三种命名方法 在程序语言中,通常使用的变量命名方法有三种:骆驼命名法(CamelCase),帕斯卡命名法(PascalCase)和匈牙利命名法. 依靠单词的大小写拼写复合词的做法,叫做"骆驼 ...
- (转载)JavaScript中定义变量
(转载)http://blog.163.com/xuxiaoqianhz@126/blog/static/165190577201061594421870/ JavaScript中定义变量有两种方式: ...
- 关于Javascript中声明变量、函数的笔记
一.概念 1.变量声明 在JavaScript中,变量一般通过var关键字(隐式声明,let关键字声明除外)进行声明,如下通过var关键字声明a,b,c三个变量(并给其中的a赋值): var a=1, ...
随机推荐
- Android学习之文件存储
•前言 任何一个应用程序,其实说白了就是在不停地和数据打交道,我们聊QQ.看新闻.刷微博,所关心的都是里面的数据, 没有数据的应用程序就变成了一个空壳子,对用户来说没有任何实际用途. 那么这些数据都是 ...
- Spring事务(三)-事务传播行为
在Spring里,一个事务方法被另外一个事务方法调用时,两个方法的事务应该如何进行,说白话一点,就是说当出现异常需要回滚时,各个方法的数据操作是否要全部回滚,事务传播行为就是决定了这样的一个处理结果. ...
- windows下如何结束Tomcat进程
问题描述: 使用IDEA启动java中的SSM项目之后,服务正常运行.操作过程中不小心把IDEA 开发工具给关闭啦,导致tomcat没有正常停止,使用的端口8080仍然被占用.再次 打开IDEA,启动 ...
- FastStone Capture 屏幕录像软件推荐 支持录像的时候放大屏幕
FastStone Capture(屏幕截图软件)v9.7中文注册版 http://www.ucbug.com/soft/25602.html
- stm32 串口DMA调试总结和反思
一 引言 最近在调试stm32的多串口.没想到居然遇到那么多的问题.这里做一个总结,还是非常有必要的. 二 问题 这里先弄清楚几个问题. 1 串口有必要使用DMA吗? DMA可以在串口高速的时候不占用 ...
- Spring Boot学习日记17
尝试整合JDBC spring: datasource: username: root password: 123456 url: jdbc:mysql://localhost:3306/mybati ...
- 实时云渲染:流式传输 VR 和 AR 内容
想象一下无需专用的物理计算机,甚至无需实物连接,就能获得高质量的 AR/VR 体验是种什么样的体验? 过去,与 VR 交互需要专用的高端工作站,并且根据头显.壁挂式传感器和专用的物理空间.VR 中的复 ...
- KingbaseES V8R6备份恢复系列之 -- system-Id不匹配备份故障
KingbaseES V8R6备份恢复案例之---system-Id不匹配备份故障 案例说明: 在KingbaseES V8R6执行备份时,在sys_log日志中出现system-id不一致的故障 ...
- KingbaseESV8R6等待事件之LWLock buffer_mapping
等待事件含义 当会话将数据块与共享缓冲池中的缓冲区关联时,会发生此等待事件. 类似Oracle cbc闩锁的是一种Kingbase的轻量级锁lwlock,这个锁的名字在不同数据库版本中可能有所不同,我 ...
- 【Java】归并排序
代码: 1 public static void mergeSort(int[] arr) { 2 if (arr == null || arr.length < 2) { 3 return; ...