var、let 和 const 的区别

var、let 和 const 的区别

关键字变量提升块级作用域重复声明同名变量重新赋值
var不是允许允许
let不允许允许
const不允许不允许
  • var声明的变量范围是函数作用域,let和const声明变量的范围是块级作用域
  • var声明的变量由变量提升,会将声明部分提升到函数作用域的顶部,let和const不具有变量提升,且具有暂时性死区特征
  • var允许在同一作用域中重复声明同一个变量,let和const不允许
  • 在全局作用域中,var声明的变量会自动成为window对象的属性,let和const不会
  • const和let两者行为基本相同,区别是const 声明的变量必须进行初始化且不可被修改

1. 作用域

1.1 函数作用域和块级作用域

函数作用域

指在程序中定义的函数内部,声明的变量只在该函数内部起作用,并紫萼在函数外部不可访问。也就是说,函数内部的变量具有局部作用域,只能在函数内部使用。

块级作用域

指在一对花括号{}中声明的变量,只能在改代码块内部起作用,并且在代码块外部不可访问。也就是说,花括号内部的变量具有局部作用域,只能在该代码块内部使用。

ES6引入了let和const关键字,JavaScript也有了块级作用域。使用 let 和 const 声明的变量拥有块级作用域,仅在包含它们的最近的一对花括号内有效。这样一来,就可以避免变量污染和冲突,在需要限制变量的作用范围时非常有用。

1.2 var 、let 和 const的声明作用域

function fn() {
            if (true) {
                var a = 10; //函数作用域,只能在fn()函数中使用
                let b = 100; // 块级作用域,只能在if代码块中使用
                console.log(a) //可以正常调用
                console.log(b)//可以正常调用
            }
            console.log(a)//a是var声明的 函数作用域 在fn函数内可以正常调用
            console.log(b)//b是let声明的 块级作用域 脱离if的大括号无法调用 会报错  b is not defined
        }
	    console.log(a) // a是var声明的 函数作用域 在函数外会报错 a is not defined
        console.log(b)//b是let声明的 块级作用域 脱离if的大括号无法调用 会报错  b is not defined
        fn()

1.3重复声明同名变量

var允许重新声明 let和const不允许重新声明

if(true) {
let a;
let a;
    //error:Identifier 'a' has already been declared 重复声明直接报错
}

if(true) {
    //const 声明的变量必须初始化,且不能重复声明
const a = 10;
const a = 20;
    //error:Identifier 'a' has already been declared 重复声明直接报错
}

var a = 10;
var a = 20;
//var声明的变量重复声明不会报错,且如果初始化后,后面的会覆盖前面的

var 和 let 同时声明同一个变量也会报错,因为它们声明的并不是不同类型的变量,只是指出变量在相关作用域如何存在,所以对声明冗余报错不会因混用而受影响。

var a;
let a;
//error:Identifier 'a' has already been declared 重复声明直接报错

2. 变量提升

var声明的变量会有变量提升特性

console.log(num1); //输出的结果undefined 原因就是因为有变量提升
var num1 = 10;

//上面的写法相当于下面
var num1;
console.log(num1); //undefined
num1 = 10;


console.log(num2); //Uncaught ReferenceError: num4 is not defined
let num2 = 10;

console.log(num3);//Uncaught ReferenceError: num4 is not defined
const num3 = 10;

3. 暂时性死区

只要作用域内存在 let、const,它们所声明的变量或常量就自动 “绑定” 这个区域,不再受到外部作用域的影响。

let a = 2;
function func() {
    console.log(a);        // 报错
    let a = 1;
}
func();
let a = 2;
function func() {
    console.log(a);        // 2
}
func();

即:只要作用域内出现了同名的 let 或 const,那么就会去找这个量(向前找),如果找不到也不会跳去外部找,只会直接报错!

只要我们遵守 “先声明后使用”,那么其实就基本不会遇到变量提升及暂时性死区问题。

4. 重新赋值

//let 声明的变量可以重新赋值
let num1 = 10;
num1 = 20;
console.log(num1);  // 20
//const 只能在声明时赋值,之后不能再重新赋值
const num2 = 10;
num2 = 20;  // Uncaught TypeError: Assignment to constant variable.

5. 全局声明

顶层对象,在浏览器环境指的是window对象,在 Node 指的是global对象。ES5 之中,顶层对象的属性与全局变量是等价的。

window.a = 1;
a // 1

a = 2;
window.a // 2

上面代码中,顶层对象的属性赋值与全局变量的赋值,是同一件事。

顶层对象的属性与全局变量挂钩,被认为是 JavaScript 语言最大的设计败笔之一。这样的设计带来了几个很大的问题,首先是没法在编译时就报出变量未声明的错误,只有运行时才能知道(因为全局变量可能是顶层对象的属性创造的,而属性的创造是动态的);其次,程序员很容易不知不觉地就创建了全局变量(比如打字出错);最后,顶层对象的属性是到处可以读写的,这非常不利于模块化编程。另一方面,window对象有实体含义,指的是浏览器的窗口对象,顶层对象是一个有实体含义的对象,也是不合适的。

ES6 为了改变这一点,一方面规定,为了保持兼容性,var命令和function命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。也就是说,从 ES6 开始,全局变量将逐步与顶层对象的属性脱钩。

var a = 1;
// 如果在 Node 的 REPL 环境,可以写成 global.a
// 或者采用通用方法,写成 this.a
console.log(window.a); // 1

let b = 1;
console.log(window.b); // undefined

上面代码中,全局变量avar命令声明,所以它是顶层对象的属性;全局变量blet命令声明,所以它不是顶层对象的属性,返回undefined

6. for循环中的var 和 let 声明的区别

for (var i = 0; i < 5; i++) {
    setTimeout( () => {
        console.log(i); // 5、5、5、5、5
    }, 0 )
}
 
for (let i = 0; i < 5; i++) {
    setTimeout( () => {
        console.log(i); // 0、1、2、3、4
    }, 0 )
}

var 是因为在退出循环时,迭代变量保存的是导致循环退出的值,也就是5.在之后异步执行超时逻辑时,所有的i都是同一个变量,因此输出的都是同一个最终值。

而在使用let声明迭代变量时,JS 引擎在后台会为每个迭代循环声明一个新的迭代变量,每个setTimeout 引用的都是不同的变量实例,所以 console.log 输出的是我们期望的值,也就是循环执行过程中每个迭代变量的值。

7. 执行速度和内存占用

JavaScript中,代码执行到变量作用域之外后,会销毁变量并释放其占用的内存。由于let和const的作用域相较于var更小,因此它们通常有更快的执行速度和较小的占用内存空间。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值