JavaScript 暂时性死区与函数作用域详解
本文深入解析 JavaScript 中的暂时性死区(TDZ)机制,阐述 let 和 const 声明变量时的封闭作用域特性。详细讨论函数参数默认值如何形成独立作用域,以及 ES6 对严格模式的限制。同时介绍 rest 参数替代 arguments 的优势及数组化操作方法。
暂时性死区
暂时性死区是指从变量进入声明所在的代码块开始,直到该变量的声明语句执行完毕为止的这段区间。在这个封闭的作用域内,变量处于不可用状态。若在声明完成前尝试访问该变量,程序将抛出错误,这种现象即被称为暂时性死区。
var声明的变量不存在暂时性死区,因为var存在变量提升机制let、const声明的变量具有块级作用域,不存在变量提升,因此存在暂时性死区
console.log(a); // 报错 Cannot access 'a' before initialization
let a = '东方不败'console.log(b); // 报错 Cannot access 'b' before initialization
const b = '东方不败'console.log(c); // undefined 因为var存在变量提升
var c = '东方求败'ES6 标准规定,若代码块中包含 let 或 const 命令声明的变量,该代码块从起始处便对这些变量形成封闭作用域。直到声明语句执行结束,这些变量才可被访问(读取或赋值),否则将引发 ReferenceError 错误。语法上将此区域称为“暂时性死区”(英文 temporal dead zone,缩写 TDZ),即代码块开始至变量声明语句完成前的区域。
函数作用域
案例一
当为函数参数设置默认值时,在函数初始化过程中,参数会形成一个独立的作用域。待初始化结束后,该作用域即行消失。这一语法行为在未设置参数默认值时不会出现。
var x = 1
function f(x,y = x){
console.log(y);
}
f(2) // 2在上述示例中,函数参数部分 (x,y = x) 构成了一个独立的作用域。此时,y 所指向的默认值 x 变量引用的是第一个参数 x,而非全局变量 x。调用 f 函数并向 x 传入数值 2 时,执行 y = x,即 y = 2,最终打印结果为 2。
案例二
let x2 = 1
function f2(y2 = x2){
let x2 = 2
console.log(y2);
}
f2() // 1调用 f2 函数时,由于未向 f2 函数传递任何参数,且 y2 = x2 形成了一个独立的作用域。在此作用域内,x2 尚未定义,因此 x2 引用的是外层全局变量 x2。执行 y2 = x2,即 y2 = 1。此时,函数内部的 x2 并未发挥任何作用。
函数执行时,会优先执行参数部分,随后才执行函数体内部代码。
// 报错
function f2(y2 = x2){
let x2 = 2
console.log(y2);
}
f2() // 报错在上述示例中,若移除全局变量 x2 则会报错,因为变量尚未声明,试图给 y2 赋值一个未声明的变量将导致错误。
var xx = 1
function fxx(xx = xx){
console.log(xx);
}
fxx()上述写法同样会报错。由于函数参数存在独立的作用域,在该参数作用域内执行结果为 let xx = xx,试图给 xx 赋值一个未声明的变量 xx 会引发错误(即暂时性死区)。
若函数的默认参数本身是一个函数,该函数的作用域同样需遵循上述规则。
let foo = 'out'
function bar(func = () => foo){
let foo = 'come'
console.log(func());
}
bar() // out此示例中,函数的参数 func 的默认值是一个匿名函数,其返回值为变量 foo。由于函数参数部分形成了一个独立的作用域,且该作用域内未定义变量 foo,因此 foo 将指向外层全局变量 foo。若移除全局变量 foo='out'(即 foo='out'),则会报错,因为赋值了一个未声明的变量。
应用程序可利用此特性实现参数默认值错误抛出:若未传入参数,则主动抛出一个错误。
function throwErr(){
throw new Error('参数不得省略')
}
function omits(mustfn = throwErr()){
return mustfn
}
omits(); // 未传参抛出错误 : 参数不得省略调用 omits 函数且未传参时,该函数将默认调用 throwErr() 函数并抛出错误。
若将参数默认值设置为 undefined,则表示该参数是可以省略的。
rest参数
arguments
arguments 可用于获取函数的参数值以及函数信息(如 name、length)等
function au(arr){
console.log('arguments:',arguments);
}
au(2,1,4,3)
可以通过数组方法对函数参数进行操作,例如进行排序。
arguments 对象并非真正的数组,而是一个类似数组的对象。为了使用数组的方法,必须使用 Array.from 先将其转换为数组。
function au(arr){
// 通过数组方法对函数参数进行排序
return Array.from(arguments).sort();
}
console.log(au(2,1,4,3)) // [1,2,3,4]
rest参数 ES6 提供了 rest 参数,语法为 (...变量名),这实际上是剩余运算符。通过 rest 参数可以很容易地对函数参数进行操作,并且
rest 的参数是一个真正的数组。
// resy参数(剩余运算符)
function residue(...val){
console.log(val); // [1,2,3]
}
residue(1,2,3)rest 参数(剩余运算符)只能放置在参数列表的最后一位,否则将报错。
// function residue2(...val,b){} // 剩余运算符不是最后一位,报错
function residue3(c,...val){
console.log(c,val); // 1 [2,3,4,5]
}
residue3(1,2,3,4,5)上述 arguments 完成的参数排序,使用 rest 可以很轻松地实现,并且语义更强,更便于阅读。
let au2 = (...val) => val.sort()
console.log(au2(2,1,4,3)); // [1, 2, 3, 4]严格模式
ES5 开始,函数内部可以设定为严格模式: function s(){ 'use strict'// 严格模式 }
// es5严格模式
function s(){
'use strict' // 严格模式
// 代码.....
}ES6 对此进行了修改,规定只要函数参数使用了默认值、解构赋值或扩展运算符,那么函数内部就不能显式设定为严格模式,否则将报错。
// es6严格模式 报错,因为设置了函数默认值
function s2(a,b = a){
'use strict'
// 代码.....
}// 报错,使用了解构赋值
const s3 = function({a,b}){
'use strict'
}// 报错,使用了剩余运算符
const s4 = (...a) => {
'use strict'
}es6 这样设置的原因是,函数内部的严格模式理应同样适用于函数体和函数参数。然而,函数执行时会先执行参数,再执行函数体,这导致了一些不严谨的情况。只有函数体中才能确定参数是否应以严格模式执行,但函数的参数却先于函数体执行,因此 es6 修改了函数参数关于严格模式的行为。
function s5(val = 070){
'use strict'
return val
}
s5() // 报错此段代码中,函数的默认值是八进制 070。在严格模式下,不能使用前缀 0 表示八进制,因此报错。但实际上,这是因为函数设置了默认参数的原因。函数先执行参数,再进入函数体,由于 es6 的限制,导致报错。
有两种方法可以规避这种限制
第一种:设置全局严格模式
'use strict'
function s6(val = 100){
console.log(val);
}
s6() // 100第二种方法:把函数嵌套在一个无参数的立即执行函数里
const s7 = () => {
'use strict'
let a;
return (function(val = 200){ return val })()
}
console.log(s7());匿名函数的调用方法:在上述示例 (function(val = 200){return val})() 中,将整个 return 的函数用 () 套起来,尾部加一个 () 调用即可,() 在函数中代表调用。
案例源码:https://gitee.com/wang_fan_w/es6-science-institute
以上就是 JavaScript 暂时性死区以及函数作用域的详细内容,更多关于 JS 暂时性死区函数作用域的资料请关注本站其它相关文章!
JS处理含Emoji或中文的字符串转数组乱码问题
2026-11-05 16:12:28
JS怎么使用展开运算符合并对象
2026-11-05 14:42:18
ES6+环境下判断JS对象为空的简洁写法
2026-11-05 12:12:23
怎么通过replacer函数自定义JS中JSON的格式化输出
2026-11-05 11:19:44
JS正则表达式基础:字面量与RegExp构造函数的区别及常用元字符
2026-11-05 11:16:58
