用最简单的例子,带你理解var、let、const以及var中的变量提升

本文详细讲解了var的变量提升机制,介绍了let和const的块级作用域以及它们与var的区别。重点讨论了let和const的声明方式、重复声明规则以及TDZ概念。通过实例揭示了JavaScript中变量声明的行为和最佳实践。
摘要由CSDN通过智能技术生成

用最简单的例子,带你理解var、let、const以及var中的变量提升

var 的变量提升机制

当我们在全局作用域或者块级(局部,函数)作用域中利用var关键字声明变量时,这个变量都会被提升到此时作用域的最顶端,这就是我们在谈到var时最经常说的变量提升。
下面我们看个例子:

const person = (status) => {
    if(status){
        var name = '前端攻城狮';
    }else{
        console.log(name);
        //undefined
    }
    console.log(name);
    //undefined
}
person(false);

上面的这段代码,很多人可能感觉习以为常了,让我们深究一下。

  • 为什么if语句都没有执行,而else语句里和最后输出的name都为undefined而不是报"name is not defined"?
    出现undefined的原因我们知道是因为变量定义而未赋值,而这里我们并没有定义变量name。
    这里是因为if代码块中的var声明的变量被提升到了函数的顶端。
  • 但是疑问又来了,为什么这里if语句没有执行,name变量会被提升到函数顶端呢?
    这是因为JavaScript引擎在代码编译时,它会自动将所有代码里面的var关键字声明的语句都会提升到当前作用域的顶端,这也就是为什么if语句没有执行,name变量会被提升到函数顶端。

其实上面的代码可以看作被解析为下面这段代码:

const person = (status) => {
    var name;
    if(status){
        name = '前端攻城狮';
    }else{
        console.log(name);
        //undefined
    }
    console.log(name);
    //undefined
}
person(false);

var在声明变量的时候存在变量提升,所以在ES6中给我们带来了块级声明。块级声明可以理解为只在当前函数下声明的变量有效,或者在代码块和{}括号里有效。
这里就引出了ES6中let声明。

let声明

let声明和var声明一致,都是用来定义变量的,但是使用let不存在变量提升,也就是说let声明的变量只在当前(块级)作用域下有效。我们将上面的例子重写一下。

const person = (status) => {
    if(status){
        let name = '前端攻城狮';
    }else{
        console.log(name);
        // name is not defined
    }
    console.log(name);
    // name is not defined
}
person(false);

我们将if条件里的var改为let,再次运行后发现,两个输出都报错。这就说明let是块级作用域,所有外面的语句块访问不到,let是没有变量提升的。

let禁止重复声明

如果在同一个作用域中某个变量已经存在,再次使用let关键字声明的话会报错。

var name = '前端攻城狮';
let name = '后端攻城狮';
// 'name' has already been declared

在不同作用域中我们再次声明:

var name = '前端攻城狮';
if(true){
    let name = '后端攻城狮';
    console.log(name)
    //后端攻城狮
}
console.log(name)
//前端攻城狮

我们发现在不同作用域中声明并不会报错,而只有在相同作用域中重复声明变量才会报错。

const声明

  • ES6中还提供了const关键字的声明。const声明指的是常量,常量就是一旦定义完就不能修改的值。还有就是常量定义时必须初始化值,如果不初始化就会报错。
  • const 也是块级作用域,const常量也只会在当前代码块内有效,也不能在当前作用域中重复定义相同的变量,也不存在变量提升。
    下面看例子:
 //定义时必须初始化
const name = '前端攻城狮';
const age; 
// Missing initializer in const declaration

//体现块级作用域
if(true){
    const name = '前端攻城狮';
}
console.log(name);
// name is not defined

//体现不能重复声明
const name = '前端攻城狮';
const name = '后端攻城狮';
// Identifier 'name' has already been declared
const 声明对象

虽然 const 变量不能修改指针,但是可以修改值,比如定义一个对象,我们可以修改对象里的属性值,但是不能重写整个对象。

const person = {
    name:'前端攻城狮',
    age:18
};
person.age = 20;//正常
person = {};//报错,不能修改对象指针

暂时锁区

跟var相比,let和const定义变量不会被提升到作用域顶端,即便是用相对安全的typeof也会出现错误。

console.log(typeof name);
// Cannot access 'name' before initialization
let name = '前端攻城狮';

上面的例子中,console.log(typeof name)报初始化前无法访问name错误。这里是因为用let定义并初始化变量语句是不会执行的。此时的value还是处于JavaScript所谓的暂时锁区(temporal dead zone),简称为TDZ中,虽然JavaScript没有明确标准TDZ,但是人们常用它描述let和const定义的变量不会提升。

TDZ的工作原理

JavaScript引擎在扫描代码时发现变量声明时,如果遇到var就会将它们提升到当前作用域的顶端,如果遇到let或const就会将声明放到TDZ中,如果访问TDZ中的变量就会抛出错误,只有执行完TDZ中的变量才会将它移出,然后就可以正常访问。这个机制也只会在当前作用域中生效。

console.log(typeof name); //undefined
if(true){
        let name = '前端攻城狮';
    }

这里注意,上面这段代码是写在在js文件中,而如果在html文件中运行,会发现输出的是string。这里name由于没有声明,它默认认去window上找,而这里碰巧window对象里有name属性,并不影响以上结论。

对于let 和 var 也可以这样理解

js编译器在编译代码块的时候会把页面整个代码块的var和let的变量都找出来,并相应地在内存开辟具名空间,不同的是var在开辟空间的时候就给它的值设置了undefined称为初始化,在执行到var的那一行代码时,再给这个变量做了赋值操作。而let是执行到该行时再执行初始化而没有赋值的操作。

写在最后

var在全局作用域声明的变量有一种行为会挂载在window对象上,它会创建一个新的全局变量作为全局对象的属性,这种行为说不定会覆盖到window对象上的某个属性,而let const 声明的变量则不会有这一行为。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

.ToString()°

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值