JS中的块级作用域(var/let/const)

本文详细介绍了JavaScript中的块级作用域,包括var声明的变量提升,let和const的块级声明,禁止重声明,const变量的特性,临时死区的概念,循环中的块作用域绑定,全局块作用域绑定,以及如何选择使用var、let和const。总结建议默认使用const,仅在需要变更变量时使用let。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、var声明及变量提升(Hoisting)机制

在函数作用域或全局作用域中通过关键字var声明的变量,无论实际上是在哪里声明的,都会被当成在当前作用域顶部声明的变量,这就是我们常说的提升(Hoisting)机制。例如:

function getValue(condition){
    if(condition){
        var value = "blue";
        return value;
    }else{
        //此处可访问变量value,其值为undefined
        return null;
    }
    //此处可访问变量value,其值为undefined
}

在预编译阶段,JavaScript引擎会将上面的getValue函数修改成下面这样:

function getValue(condition){
    var value; //变量声明被提升到函数顶部
    if(condition){
        value = "blue";
        return value;
    }else{
        return null;
    }
}

变量value的声明被提升至函数顶部,而初始化操作依旧留在原处执行,这就意味着在else子句中也可以访问到该变量,且由于此时变量尚未初始化,所以其值为undefined。

2、块级声明let

块级声明用于声明在指定块的作用域之外无法访问的变量。块级作用域存在于:

  • 函数内部
  • 块中(字符 “{” 和 “}” 之间的区域)

 let 声明的用法和var相同。用let代替var来声明变量,就可以把变量的作用域限制在当前代码块中。由于let声明不会被提升,因此开发者通常将let声明语句放在封闭代码块的顶部,以便整个代码块都可以访问。

function getValue(condition){
    if(condition){
        let value = "blue";
        return value;
    }else{
        //变量value在此处不存在
        return null;
    }
    //变量value在此处不存在
}

3、禁止重声明

var count = 30;
let count = 40;//抛出语法错误

 4、const声明

(1)const声明的变量必须进行初始化,且一旦设定后不可更改。

(2)const与let声明的都是块级标识符,所以常量也只在当前代码块内有效,一旦执行到块外会立即被销毁。常量同样也不会被提升至作用域顶部。

if(condition){
    const maxItems = 5;
}
//此处无法访问maxItems

在这段代码中,在if语句中声明了常量maxItems,语句执行一结束,maxItems即刻被销毁,在代码块外访问不到这个常量。

(3)const声明对象,不允许修改绑定,但允许修改属性值。

const person={
    name:"yxy"
}
person.name="yff";//可以修改属性值
person={
    name:"Greg"//抛出语法错误
}

5、临时死区(Temporal Dead Zone)

与var不同,let和const声明的变量不会被提升到作用域顶部,如果在声明之前访问这些变量,即使是相对安全的typeof操作符,也会触发引用错误。

(1)触发引用错误

if(condition){
    console.log(typeof value);//引用错误
    let value = "blue";
}
  • 由于console.log(typeof value)语句会抛出错误,因此用let定义并初始化变量value的语句不会执行。
  • 此时的value位于JavaScript社区所谓的“临时死区”中。
  • JavaScript引擎在扫描代码发现变量声明时,要么将它们提升至作用域顶部,要么将声明放入TDZ中,访问TDZ中的变量会触发运行时错误。只有执行过变量声明语句后,变量才会从TDZ中移出,然后方可正常访问。

(2)不会触发报错的场景

console.log(typeof value);//引用错误
if(condition){
    let value = "blue";
}

在let声明的作用域外对该变量使用typeof则不会报错。因为此时value并不在TDZ中,这也就意味着不存在value这个绑定,typeof操作最终返回“undefined"。

6、循环中的块作用域绑定

把声明的计数器变量限制在循环内部。

for(var i=0;i<10;i++){
    process(items[i]);
}
console.log("i:"+i);//i在此处仍可以访问

在JavaScript中,由于var声明得到了提升,变量i在循环结束后仍可访问。如果换用let声明变量就能得到想要的结果。

for(let i=0;i<10;i++){
    process(items[i]);
}
console.log("i:"+i);//i在此处,不可以访问。会抛出一个错误

7、全局块作用域绑定

当var被用于全局作用域时,它会创建一个新的全局变量作为全局对象(浏览器环境中的window对象)的属性。即用var很可能会无意中覆盖一个已经存在的全局属性。

var RegExp = "hello";
console.log(window.RegExp);//"hello"
console.log(window.RegExp === RegExp);//true

var yff = "hi";
console.log(window.yff);//"hi"
console.log(window.yff === yff);//true

但如果你在全局作用域使用let或const,会在全局作用域下创建一个新的绑定,但该绑定不会添加为全局对象的属性。即,用let或const不能覆盖全局变量,只能遮蔽它。

let RegExp = "hello";
console.log(window.RegExp);//"hello"
console.log(window.RegExp === RegExp);//false

const yff = "hi";
console.log(window.yff);//"hi"
console.log(window.yff === yff);//false

 总结:var声明的变量会创建全局对象属性(window),而let和const不会。

如果希望在全局对象下定义变量,仍可以使用var,这种情况常见于在浏览器中跨frame或跨window访问代码。

8、总结

当前使用块级绑定的最佳实践是:默认使用const,只在确实需要改变变量时使用let。这样就可以在某种程度上实现代码的不可变,从而防止某些错误的产生。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值