JavaScript 各声明var、let、const方式区别『详解』

在这里插入图片描述

JavaScript 变量声明三种方式的区别『var、let、const』


一、变量声明

变量的声明在每个编程语言里都会有,JavaScript的变量声明有varlet,常量的声明是const。这几种声明方式的作用域有一定差别,以var举例,以var声明的变量会被隐式地创建为全局变量,并且还会产生变量提升的效果。


二、变量提升🎈

在讲解 varletconst 之前应了解什么是变量提升,这很重要。

先来看看这几行代码,判断是否能够正确打印不抛出异常

 // 代码片段一
 a = 1;
 console.log(a);	// 打印:1

 // 代码片段二
 var a = 1;
 console.log(a)		// 打印:1

 // 代码片段三
 console.log(a)		// 打印:undefined
 var a = 1;

 // 代码片段四
 console.log(a)		// 抛出异常

 // 代码片段五
 var a = 1;
 {
     console.log(a)	// 打印:1
 }

 // 代码片段六
 console.log(a)		// 打印:undefined
 {
     var a = 1;
     console.log(a)	// 打印:1
 }
  • 代码片段一: 即使没有使用声明符也能正常赋值和输出,这是因为在解析JavaScript代码的时候,将变量提前以var的方式进行了声明。
  • 代码片段二: 正常以var进行声明的方式,结果当然能输出。
  • 代码片段三: 神奇的是,程序也能正常输出并且不会抛出异常,但输出结果为undefined,这印证了在代码片段一中所说的,将变量提前以var的方式进行了声明。
  • 代码片段四: 二话不说总结打印一个为定义的变量时,那就不存在提前声明了,会抛出 ReferenceError异常。
  • 代码片段五: 对于块级代码块,能够调用全局的声明。
  • 代码片段六: 即使 var 声明变量写在了块里,变量依旧可以在声明之前使用,存在变量提升的现象。

再来看看其他声明方式的效果

 // let 声明变量
 console.log(a);
 let a = 1;
 console.log(a);

 // const 声明变量
 const a = 1;
 console.log(a);

以这两种方式声明,在声明之前调用变量会抛出 ReferenceError 异常,只有在声明之后调用不会异常,可见这两种方式是不存在变量提示的。

总结

  • varfunction 在作用域内 存在着变量提升。
  • 变量提升将影响变量声明,而不会影响其值的初始化,在赋值之前,值为undefined
  • function的变量提升能够使函数在声明前能够被调用。
  • letconst这两种声明变量的方式不会造成变量提升。

三、var 变量声明

var 声明一个变量,可同时将其初始化为一个值,它是一个全局作用域的声明方式,也被函数作用域限制。但因其全局作用,以及提前声明的特性,容易造成变量的全局污染,造成意想不到的后果。

1、声明一个变量

// 声明一个变量
var a;

2、声明并初始化一个变量

// 声明并初始化一个变量
var a = 1;

3、声明并初始化两个个变量

// 声明并初始化两个个变量
var a = 1, b = 2;

4、多个变量的初始化

// 多个变量的初始化
var a;
function f() {
    var a = b = 1
}
f()
console.log(a)      // 打印:undefined
console.log(b)      // 打印:1

在函数里以var a = b = 值的方式将变量ab同时赋值为了1,这种操作等用于var a = 1; b = 1。但调用函数后,函数外打印打印这两个值时能正确执行值不相等

  • a、先来讲讲为什么说能正确执行,b 变量只在函数中出现过,根据局部能调用全局作用域,全局不能调用局部作用域的特点,b 应该会抛出未定义的异常才对,但实际上并没有这样。这是因为在函数内部初始化变量b的时候,并未指定声明符,那么JavaScript将变量提前以var的方式进行了声明,而这个提升是全局的。
  • b、 再来说说为什么 a != b,其实很简单,在程序最开始就声明了变量var a,而执行函数时并没有将a的值修改为1,这是因为被函数作用域所限制,1这个值只在函数中生效。

5、意想不到的全局污染

for (var i = 1; i <= 10; i++) {
    // 延时一秒后执行
    setTimeout(function () {
        console.log(i);		// 输出十次11
    }, 1000)
}

var变量在使用循环时搭配定时器,会产生意想不到的效果,程序的目的是在一秒之后分别输出1-10的值,结果却是输出十次11。var 改成 let则恢复正常

var x = 1;

function func1() {
    var y = 2;

    function func2() {
        x = 4;
        y = 5
        z = 6;
    }

    func2()
    console.log(x, y, z)	// 打印:4, 5, 6
}

func1()
console.log(x)				// 打印:4

上述代码,xyz打印的值各是多少呢?将xyz分开来看。

  • 对于x来说它是写在所有函数之前的全局变量,对x的修改是全局的,x的最后一次修改是在func2函数里,那么x的值到最后应该为4。
  • 对于y来说,是在func1函数中声明的,那么其作用域只在func1函数中生效,y的最后一次修改是在func2函数里,那么y的值到最后应该为5。
  • z的值只出现过一次赋值,在此之前并没有任何的变量声明,在func2函数中以z = 5表达式赋值为5,其写法相同于var z = 5,那么z的值到最后应该为6。

6、可重复声明

 // 重复声明
 var a = 1;
 var a = 2;
 console.log(a)      // 打印最后赋的值:2

 // 循环重复声明
 var x;
 for (var i=1; i <= 5; i++) {
     var x = i;
 }
 console.log(x)      // 打印最后赋的值:5

使用var声明变量,是可以重复声明的,这有个好处就是写起来比较灵活,不会抛出重复声明的异常。


四、let 变量声明

let 是块级作用域的本地变量。与 var 声明不同的是,let不允许重复声明同一个变量,将会抛出 SyntaxError 异常,但可以修改变量的值。

// 块级作用域
{
    let a = 1;
    console.log(a)          // 打印:1
    a = 2;
    // let a = 3;              // 抛出 SyntaxError
}
console.log(a)              // 抛出异常 ReferenceError

let 声明的变量不存在变量提升,在变量初始化前访问该变量会抛出 ReferenceError 异常,自块开始到在初始化之前属于暂存死区。

// 不存在变量提升
console.log(a)         // 抛出 ReferenceError
let a = 1;

{
    console.log(b)     // 抛出 ReferenceError
    let b = 2;
}

使用 let 声明的变量能够解决 三、var 变量声明 ⇨ 5、意想不到的全局污染 中所提到的全局污染。

  • 此时程序能正确输出我们所期望的值,1-10之间的数。

     for (let i = 1; i <= 10; i++) {
         // 延时一秒后执行
         setTimeout(function () {
             console.log(i);		// 输出1-10
         }, 1000)
     }
    
  • 此时每个变量在各自的作用域里各司其职,河水不犯井水,对于我们编写代码来说不会出现一些意外的值。

    let x = 1;
    
    function func1() {
        let y = 2;
    
        function func2() {
            let x = 4;
            let y = 5
            let z = 6;
            console.log(x, y, z)    // 打印:4, 5, 6
        }
    
        func2()
        console.log(x, y)	        // 打印:1 2
    }
    
    func1()
    console.log(x)				    // 打印:1
    

五、const 常量声明

const 是块级作用域的本地常量。与变量不同的是,常量不支持重复声明也不支持修改,若是存在修改行为则会抛出 TypeError 异常。

// 块级作用域
{
    const BASE = "块级";
    console.log(BASE)        // 正常打印
    // const BASE = "重复赋值"   // 抛出 SyntaxError
    // BASE = "修改";           // 抛出异常 TypeError
}
console.log(BASE)           // 抛出异常 ReferenceError

常量名和其他声明方式一样,都可以大小写字母,但程序员之间会使用全大写进行命名,这是为了便于辨别常量类型,可以说是程序员之间的一种默契。

// 声明常量
const BASE = "常量名大写"
// 声明并初始化两个个变量
const BASE_NAME1 = 1, BASE_NAME2 = 2;

const 常量不存在变量提升,在常量初始化前访问该常量会抛出 ReferenceError 异常,自块开始到在初始化之前属于暂存死区。

// 不存在变量提升
console.log(a)         // 抛出 ReferenceError
const a = 1;

{
    console.log(b)     // 抛出 ReferenceError
    const b = 2;
}

对于性能来说,const 不允许重新赋值的,所以转换逻辑很简单,其潜在优化比较好。但是这种差异是细微的,几乎察觉不到,使用常量更多是注重代码风格,便于阅读。


六、var、let和const应用场景

声明描述作用域是否存在变量提升
var声明一个变量,可同时将其初始化为一个值全局作用域
let声明一个块级本地变量,可同时将其初始化为一个值[块级/局部]作用域
const声明一个只读的命名常量[块级/局部]作用域
  • 如果一个变量在不会发生改变,在大多数情况下应使用 const 进行声明将其变为常量。这样有利于代码的阅读,不用考虑这个变量会不会发生改变,还能提高程序的 潜在 执行效率。
  • 大部分情况下都不应该使用 var 进行声明,因其全局破坏的影响,尽量使用 letconst 进行取缔。
  • 在使用 WebStorm 编辑器进行代码书写时,使用 var 进行声明会有明确的弱警告提示。
    在这里插入图片描述

参考资料


推荐文章🍗

😝给笔者加个🍗

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值