ES6的块级作用域和暂时性死区是什么?
「这是我参与2022首次更文挑战的第15天,活动详情查看:2022首次更文挑战」
在上篇文章中我们了解了函数作用域和全局作用域,今天我们将介绍块级作用域和暂时性死区。
作用域
简单来说,作用域就是用来规定变量的作用范围的,在任何语言当中都会有作用域的概念,在ES6以前,JavaScript只有函数作用域和全局作用域,ES6之后又增加块级作用域。
函数作用域和全局作用域
块级作用域和暂时性死区
在ES6中带来了const和let变量声明的新关键字和块级作用域,在其他语言中普遍都存在的概念,在这之前JavaScript并没有块级作用域的概念,随着块级作用域的还有“暂时性死区”的概念,说到这个,我们要先从JavaScript的“变量提升”来说起
请看下面的代码
function foo() {
console.log(bar)
var bar = 3
}
foo()
如果你对JavaScript有一定的了解,就会知道上面的代码输出undefined,因为bar变量在函数作用域内进行了提升。
上面的代码和下面的代码是等价的
function foo() {
var bar
console.log(bar)
bar = 3
}
foo()
但当使用let对bar变量进行声明时,则会报错Uncaught ReferenceError: Cannot access 'bar' before initialization
,请看下面代码:
function foo() {
console.log(bar)
let bar = 3
}
foo()
我们知道,使用let或者const声明变量时会针对这个变量形成一个封闭的块级作用域,在这个块级作用域中,如果在声明变量前访问这个变量,就会报ReferenceError
错误;如果在声明变量后访问,则可以正常获取变量值,如下:
function foo() {
let bar = 3
console.log(bar)
}
foo()
上面的代码将正常输出3。所以,在相应花括号形成的作用域中存在一个“死区”,开始于函数的开头,结束于相关变量声明语句的所在行。在这个范围内无法访问使用let或者const声明的变量。这个“死区”的专业名称为TDZ(Temporal Dead Zone)。
文字描述不好理解,看下图更容易理解:
在foo函数中,let bar = 3
这一行的前面的区域称为“死区”,在“死区”内访问变量bar会报错,而在“死区”外可正常访问。
上图中圈出的暂时性死区,也有一种比较极端的场景,函数的参数默认值也会收到它的影响,比如下面的代码:
在下面的foo函数中,如果没有传入第一个参数,则会使用第二个参数作为第一个实参。
function foo(arg1 = arg2, arg2) {
console.log(arg1, arg2)
}
foo('arg1', 'arg2')
运行上面代码,返回正常。
但当第一个参数为默认值时,执行arg1 = arg2
会被当作暂时性死区处理,代码如下:
function foo(arg1 = arg2, arg2) {
console.log(arg1, arg2)
}
foo(undefined, 'arg2')
// Uncaught ReferenceError: Cannot access 'arg2' before initialization
上面代码运行报错是因为除了块级作用域以外,函数参数默认值也会受到暂时性死区影响。
我们来再看一个例子,下面的代码将输出什么呢?
function foo(arg1 = arg2, arg2) {
console.log(arg1, arg2)
}
foo(null, 'arg2')
上面代码执行将输出null, arg2
。这里就涉及到undefined和null的区别了。在执行foo(null, 'arg2')
时,函数并不会使用arg1的默认值,而是直接接受null
作为参数arg1的值。
我们再来看另外一个场景
function foo(arg1){
let arg1
}
foo('arg1')
// Uncaught SyntaxError: Identifier 'arg1' has already been declared
运行这段代码将会报上面的错误,因为函数参数名出现在其“执行上下文/作用域”导致的。
就像下面函数第一行已经声明了arg1变量,函数内再用let声明就会报错,这也是let声明变量的特点。
function foo(){
var arg1
let arg1
}
foo('arg1')
// Uncaught SyntaxError: Identifier 'arg1' has already been declared
通过debugger可以发现,函数的参数变量存在于的函数作用域内。
在下篇文章中,我们将介绍JavaScript的执行上下文和调用栈的相关内容。
欢迎我的公众号【小帅的编程笔记】,让自己和他人都能有所收获!
转载自:https://juejin.cn/post/7064929997983514661