ECMAScript 变量是松散类型的,意思是变量可以用于保存任何类型的数据。每个变量只不过是一个用于保存任意值的命名占位符。
变量声明
var
var
声明语句声明一个变量,并可选地将其初始化为一个值。- 变量声明无论发生在何处,都在执行任何代码之前进行处理(即变量提升)。
var
声明的变量的作用域是它当前的执行上下文(全局作用域或函数作用域)- 声明和非声明的变量存在以下3点差异:
- 声明变量的作用域限制在其声明位置的上下文中,而非声明变量总是全局的。
- 声明变量在任何代码执行前创建,而非声明变量只有在执行赋值操作的时候才会被创建(非声明变量在未赋值前调用,将会抛出错误)。
- 声明变量是它所在上下文环境的不可配置属性,非声明变量是可配置的(如非声明变量可以被删除)。
let
let
语句声明一个块级作用域的本地变量,并且可选的将其初始化为一个值。与var
关键字不同的是,var
声明的变量只能是全局或者整个函数块的。let
不会在全局声明时(在最顶部的范围)创建window
对象的属性。- 在同一个函数或块作用域中重复声明同一个变量会引起
SyntaxError
。 - 通过
let
声明的变量直到它们的定义被执行时才初始化。在变量初始化前访问该变量会导致ReferenceError
。该变量处在一个自块顶部到初始化处理的**“暂存死区”**中。
const
const
语句声明一个块级作用域的本地常量,并且必须将其初始化为一个值。但常量的值是无法(通过重新赋值)改变的,也不能被重新声明。const
特性除不能被重新被赋值外,其他特性基本与let
相似。const
的值是一个引用时,只要不修改其引用,可以对引用值内容进行操作。
变量提升(Hoisting)
变量提升(Hoisting)被认为是, Javascript中执行上下文 (特别是创建和执行阶段)工作方式的一种认识。在 ECMAScript® 2015 Language Specification 之前的JavaScript文档中找不到变量提升(Hoisting)这个词。
变量提升是指将变量和函数的声明提升到作用域顶部。注意提升的只是声明,定义并没有被提升。如语句var a = 2
会被拆分为var a
和 a = 2
,变量声明var a
会被提升到作用域顶部,而定义a = 2
位置不变。
变量提升的本质其实是js引擎在编译的时候,就将所有的变量声明了,因此在执行的时候,所有的变量都已经完成声明。
将变量、函数的整个产生的过程分为【创建、初始化和赋值】
对于var
变量,其过程如下:
1.进入一个作用域(全局作用域或函数作用域);
2.找到当前作用域中所有用var
声明的变量,在这个环境中「创建」这些变量;
3.将这些变量「初始化」为undefined
。
4.开始执行代码
5.当执行到变量赋值语句时,开始赋值
也就是说var
声明会在代码执行之前就将「创建变量,并将其初始化为undefined
」。
对于function
函数,其过程如下:
1.找到所有用function
声明的变量,在环境中「创建」这些变量。
2.将这些变量「初始化」并「赋值」为function(){ // ... }
。
3.开始执行代码
也就是说function
声明会在代码执行之前就「创建、初始化并赋值」。对于
let
变量,其过程如下:
1.找到所有用let
声明的变量,在环境(块作用域)中「创建」这些变量
2.开始执行代码(注意现在还没有初始化)
3.代码运行到声明处,如let x = 1
,此时将x
「初始化」为1
,如果没有初始值,则初始化为undefined
4.如果再执行到如x = 2
的语句时,才是对x
进行「赋值」
对于const
常量,它与let
的区别是没有「赋值」的过程。
总结:
1.var
的「创建」和「初始化」都被提升了。
2.function
的「创建」「初始化」和「赋值」都被提升了。
3.let / const
的「创建」过程被提升了,但是初始化没有提升。所以let和const声明的变量也是有变量提升的
暂时死区TDZ(Temporal Dead Zone)
暂时死区,在网上也有很多叫暂存(性)死区。
由let/const
声明的变量,当它们包含的词法环境(Lexical Environment)被实例化时会被「创建」,但只有在变量的词法绑定(LexicalBinding)已经被求值运算【初始化】后,才能够被访问。也就是说,在程序中,变量「创建」之后「初始化」之前的这部分区域,该变量是不能被访问的,对这个变量而言,这是它的暂时死区TDZ。
以let/const
声明的变量或常量,必需是经过对声明的赋值语句的求值后,才算初始化完成,创建时并不算初始化。如果以let声明的变量没有赋给初始值,那么就赋值给它undefined
值。也就是经过初始化的完成,才代表着TDZ期间的真正结束,这些在作用域中的被声明的变量才能够正常地被访问。
块作用域
被大括号(“{}”)包裹起来的指令集合(代码块)形成一个块作用域。对于function语句和for语句,括号内声明的变量的作用域在紧跟其后的块作用域中。
function fn(a, b) {
// a 和 b 的作用域在这个块作用域中
}
for(let x = 0; x < 10; x++) {
// x 的作用域在这个块作用域中
}