一. 变量提升
变量提升:
造成变量提升的本质原因是js引擎在代码执行前有一个解析的过程,创建了执行上下文,初始化了一些代码执行时需要用到的对象。这个变量对象是执行上下文的一个属性,它包含了函数的形参、所有的函数和变量声明,这个对象的是在代码解析的时候创建的。
具体过程:
JS 在遇到一个变量或者一个函数时,会有两步操作:解析和执行!!
(1) 解析过程
-
JS 会对语法进行检查,并对函数进行预编译!!
-
会创建一个全局执行上下文环境
-
并创建该函数执行上下文的变量对象 AO
- 在该变量对象 AO 中,会把代码中的变量、函数声明都找到,并作为属性添加到该函数中,并赋值为 undefined
补:
(2) 为什么要进行变量提升?
- 提高性能,可以为变量提前分配栈空间!!
在 JS代码执行之前,会进行语法检查和预编译,并且这一操作只进行一次。这么做是为了提高性能。如果没有这一步,那么每次执行代码前都必须重新解析一遍该变量(函数),而这时没有必要的。
- 容错性更好
(3) 缺点:
- 导致内存泄漏(意外声明全局变量)
- 出现意外的错误
如上:本来是想打印外层的 temp 变量,但打印出来的确是 undefined ,因为变量提升的问题!!
二. 变量声明
三种常见的声明:var
、 let
、 const
是否有变量提升 | 作用域 | 是否可重复定义 | 是否会成为windows对象的属性 | |
---|---|---|---|---|
var | 有 | 函数作用域 | 可重复声明 | 会 |
let | 没有 | 块级作用域 | 不可重复 | 不会 |
const | 没有 | 块级作用域 | 不可重复 | 不会 |
var
不初始化的话,会默认是 undefinedlet
没有变量提升,会出现 “暂时性死区”,即在let
声明之前的执行瞬间被称为"暂时性死区",在此阶段引用后面声明的变量都会报错!!!const
声明的变量必须进行初始化!!!- 只有
var
声明的变量,会在代码执行前(解析时)被当作属性加入到提前创建的变量对象 AO中!! 《1》《2》
《1》function foo() {
console.log(a);
a = 1;
}
foo(); // 报错,函数中的 "a" 并没有通过 var 关键字声明,所有不会被存放在 AO 中。【没有变量提升,执行时代码需要一步一步】
《2》function foo() {
console.log(a);
let a = 1;
}
foo(); // 报错,函数中的 "a" 并没有通过 var 关键字声明,所有不会被存放在 AO 中。
《3》function foo() {
console.log(a);
var a = 1;
}
foo(); // undefined
- 直接 a = 1 ,会使变量成为全局变量,即会成为 window对象 的属性
const 声明的变量的值可以改变么?
const保证的并不是变量的值不能改动,而是变量指向的那个内存地址不能改动。
- 对于
基本类型的数据(数值、字符串、布尔值)
,其值就保存在变量指向的那个内存地址,因此等同于常量。《1》 - 但对于
引用类型的数据(主要是对象和数组)
来说,变量指向数据的内存地址,保存的只是一个指针,const只能保证这个指针是固定不变的,至于它指向的数据结构是不是可变的,就完全不能控制了。《2》《3》
《1》
const a = 3;
a = 4;
console.log(a); // 报错
《2》
const arr = [1,2,3];
arr = [];
console.log(arr); // 报错
《3》
const arr = [1,2,3];
arr .push(2);
console.log(arr); // [1,2,3,2]