此文章主要是想指出,通常我们认为js中 var定义的变量会发生声明提升,而let/const 定义的变量不会发生声明的提升
其实:let/const/var
定义的变量 都会发生声明提升
声明提升
先来看个例子
var a = 1
;(function() {
console.log(a)
let a = 2
})()
如果没有发生变量声明提升,那么console.log应该输出 1
但实际上此处发生了 ReferenceError(引用错误) 表明一个不存在的变量被引用。
那麽:var/[let、const] 声明的变量在发生声明提升时,初始化行为不同
-
var 声明的变量初始化为
(undefined)
-
let/const 声明的变量会保持为未初始
(uninitialised)
化状态
(function() {
console.log(a) // undefined, a 在这里会被初始化为 undefined
console.log(b) // ReferenceError, b 在这里会保持为未初始化的状态不变
var a = 1
let b = 2
})()
TDZ
概念:TDZ 并不是某个地方, 或是内存中的某个区域,而是变量被声明和被初始化之间的这段时间
例子:
let a = "outer"
;(function() {
// 内部的 a 变量在这里被声明,TDZ 开始的地方
console.log(a) // ReferenceError
let a = "inner" // a 变量被初始化,TDZ 结束
})()
稍微复杂一点的例子:
var a = a // 没有问题
let b = b // ReferenceError,在这里b并没有完全的被初始化,还处在 TDZ 当中
ES6 中函数默认参数的计算顺序是从左到右进行的
// 函数的参数 a 想要得到参数 b 的值,而参数 b 这时还未被初始化, 处在 TDZ 中, 所以会报 ReferenceError 的错误
function foo(a = b, b) {
console.log(a)
}
foo(undefined, 1)
//
function foo(a, b = a) {
// 这里的 a 已经被初始化,所以 b 可以取到 a 的值
console.log(b)
}
foo(1, undefined)
看了以上的例子估计还是不能理解,为什么会是这样子的? 函数参数的默认值有自己的作用域?
再看看下面的三个例子:
let y = 1;
function foo(x = function(){console.log(y)}, y = 2) {
x(); // 2
y = 3;
x(); // 3
}
foo();
console.log(y); //1
let y = 1;
function foo(x = function(){console.log(y)}) {
let y = 3;
x(); // 1
}
foo();
function foo(x = function(){console.log(y)}) {
let y = 3;
x(); // ReferenceError: y is not defined
}
foo();
很让人困惑,感觉是存在3个作用域,全局/参数/函数体。参数默认值的函数,可以访问参数中定义的,和参数外定义(outer/global)的变量,不能访问函数体中定义的变量。 一个需要注意的地方是,如果参数默认值是一个变量,则该变量所处的作用域,与其他变量的作用域规则是一样的,即先是当前函数的作用域,然后才是全局作用域。
参数在有默认值的情况下,此时会创建一个新的作用域,这个作用域既不是函数作用域,也不是全局作用域。
// 默认值,并不会肯定从全局去取,而是从 params scope 中取
var x = 1;
function foo(x = x) { // throws!
console.log(x);
}
foo()
总结
-
如果参数存在默认值,则有三个环境 environment( environment in ES6 = scope in ES5).
Outer environment
/parameters environment
/function body environment
. -
parameters environment 可以访问自己和外层,不能访问函数体内的变量。
-
函数体内可以修改 parameters env 里定义的 formal parameters 的值,不能重新定义(除非用var……)。
提示
参数变量、函数体内变量、全局变量别用一样的名字
不要用 var 来定义变量,总是用 let 或 const
思考
// reference error: x is not defined. 因为这里 x 是处在 TDZ(Temporal Dead Zone)。
var x = 1;
function foo(x = x) {
console.log(x)
}
foo();
参考文献: