变量提升
var
变量提升即将变量声明提升到它所在作用域的最开始的部分(这里就要先搞清楚它的作用域)
1 | (function(){ |
1 | var scope="global"; |
由于有变量提升的问题,在写js code 的时候,我们需要把变量放在函数级作用域的顶端。
注意: 使用 “use strict” 如果将值分配给一个未声明的变量会自动创建该名称的全局变量,则会报错。
let
-
不存在变量提升
-
存在块级作用域
1
2
3
4
5
6{
var a = 10;
let b = 20;
}
console.log(a); //10
console.log(b); // ReferenceError: b is not defined -
let
不允许在同一作用域内重复声明同一个变量 -
可以先声明后初始化
const
- 不存在变量提升
- 一旦声明常量,就必须同时初始化。不能先声明,后初始化
- 不允许在同一作用域内重复声明同一个变量
- 声明的常量不能改变,而修改对象的属性,并不会改变对象的地址,因此用const声明对象的属性是可以修改的
⚠️⚠️注意 var 在全局作用域下声明的变量会挂载到全局window上,但是let 和 const 的不会!!
1 | let len = 10; |
函数提升
1 | function myTest(){ |
但是如果是这样则会产生错误:
1 | console.log(sum(10,10)); |
因为函数位于一个初始化的语句中,是函数声明,而不是函数声明
同时也要记住,即使是具名的函数表达式,名称标识符在赋值之前也无法在所在作用域中使用:
1 | foo(); // TypeError |
这个代码片段经过提升后,实际上会被理解为以下形式:
1 | var foo; |
var let const 如何选择
var
< let
< const
即为了避免代码的不规范及全局变量污染,优先使用 const
,,其次是 let
,最后才是 var
。
函数优先
函数声明和变量声明都会被提升。但是一个值得注意的细节(这个细节可以出现在有多个
“重复”声明的代码中)是函数会首先被提升,然后才是变量。
看一下代码:
1 | console.log(foo); //ƒ foo(){} |
由此可以看出,同样会有提升,但是函数的提升会优先
背后的原理
为什么会存在变量的提升呢?内部的运行机制是怎么处理的?
在真正解释执行之前,JavaScript解释器会预解析代码,将变量、函数声明部分提前解释:
- 运行代码来调用函数
- 在执行代码函数执行,先去创建上下文.
- 进入创建阶段
- 初始化
作用域链
- 创建变量对象(VO)
- 创建
arguments对象
,检查当前上下文的参数,初始化名称和值并创建引用副本 - 扫描当前上下文的函数声明
- 扫描到来函数声明,那就在
VO(variable object)
里创建他的名称,并且指针是指向内存中函数引用的地址 - 如果扫描的函数声明已存在,那就覆盖
VO
中的引用指针
- 扫描到来函数声明,那就在
- 扫描当前上下文的变量声明
- 扫到变量声明,在
VO
中添加变量名称,并且初始化值是undefined
- 如果
VO
中存在变量名称,那就不做处理,继续扫描
- 扫到变量声明,在
- 创建
- 确定当前上下文的
this
指向
- 初始化
- 激活/执行阶段
比如:
1 | // 上下文 |
可以看出为什么会出现提升且为什么函数可以优先提升。
1 | (function() { |
最后,欢迎大家围观指正