编辑
2023-07-11
JS基础
00
请注意,本文编写于 499 天前,最后修改于 483 天前,其中某些信息可能已经过时。

目录

作用域
变量提升
暂时性死区
重复声明
常量性
总结

letconstES6中引入的两种新的变量声明方式,它们与var有一些重要的区别,本质上它们的出现就是为了替代var,本文将从实现原理的角度来深入探讨let/const为什么比var更好。

你可能已经听说过ES6引入了两种新的声明变量的方式:letconst。你可能也知道它们有一些优点,比如变量不会提升、有块级作用域、const不可修改等。本文将揭开let/const的秘密,让你深入理解它们的优势。

作用域

var声明的变量属于函数作用域或全局作用域,也就是说,它们在声明所在的函数或全局环境中都是可见的,而不受块级结构(如iffortry等)的限制。例如:

js
function foo() { var x = 1; if (true) { var x = 2; // 同一个变量 console.log(x); // 2 } console.log(x); // 2 }

letconst声明的变量属于块级作用域,也就是说,它们只在声明所在的块级结构中有效,而在外部不可见。例如:

js
function foo() { let x = 1; if (true) { let x = 2; // 不同的变量 console.log(x); // 2 } console.log(x); // 1 }

这样的设计可以避免var声明的变量污染全局或函数作用域,造成意料之外的结果。同时,也可以让代码更加清晰和模块化,因为每个块级结构都可以有自己的局部变量。

变量提升

var声明的变量会发生变量提升(hoisting),也就是说,无论在哪里声明,都会被视为在作用域的最开始处声明。这意味着,可以在声明之前使用变量,但是此时变量的值是undefined。例如:

js
function foo() { console.log(x); // undefined var x = 1; }

letconst声明的变量不会发生变量提升,也就是说,它们只在声明之后才能使用,否则会报错。例如:

js
function foo() { console.log(x); // ReferenceError: x is not defined let x = 1; }

这样的设计可以避免var声明的变量由于提升而导致的逻辑错误或隐蔽的bug。同时,也可以强制让代码更加规范和严谨,因为必须先声明后使用变量。

暂时性死区

由于letconst声明的变量不会发生变量提升,所以在声明之前使用它们会报错。但是这并不意味着它们在声明之前就不存在,事实上,它们已经存在于块级作用域中,只是处于一种不可访问的状态,这就是所谓的暂时性死区(temporal dead zone)。例如:

js
function foo() { console.log(typeof x); // ReferenceError: x is not defined let x = 1; }

如果letconst声明的变量在声明之前不存在,那么上面的代码应该输出undefined,而不是报错。这说明x已经存在于块级作用域中,但是由于暂时性死区的原因,不能被访问。

暂时性死区的存在可以避免letconst声明的变量被意外覆盖或污染。例如:

js
var x = 1; function foo() { if (true) { // 这里存在暂时性死区 console.log(x); // ReferenceError: x is not defined let x = 2; // 不会覆盖全局变量x } }

如果没有暂时性死区,那么上面的代码会输出1,而不是报错。这说明let声明的x并没有覆盖全局变量x,而是在块级作用域中创建了一个新的变量x

重复声明

var声明的变量可以在同一个作用域中重复声明,后面的声明会覆盖前面的声明。例如:

js
function foo() { var x = 1; var x = 2; // 重复声明 console.log(x); // 2 }

letconst声明的变量不能在同一个作用域中重复声明,否则会报错。例如:

js
function foo() { let x = 1; let x = 2; // SyntaxError: Identifier 'x' has already been declared }

这样的设计可以避免var声明的变量由于重复声明而导致的混乱和错误。同时,也可以保证letconst声明的变量的唯一性和稳定性。

常量性

varlet声明的变量都是可变的,也就是说,可以对它们进行重新赋值。例如:

js
function foo() { var x = 1; x = 2; // 可以重新赋值 console.log(x); // 2 }

const声明的变量是常量,也就是说,一旦声明,就不能对它们进行重新赋值。否则会报错。例如:

js
function foo() { const x = 1; x = 2; // TypeError: Assignment to constant variable. }

这样的设计可以保证const声明的变量的不可变性和恒定性,避免了由于重新赋值而导致的逻辑错误或隐蔽的bug。同时,也可以让代码更加清晰和可读,因为可以一目了然地知道哪些变量是不会改变的。

需要注意的是,const声明的变量只保证了它们的引用不可变,而不保证它们所指向的值不可变。也就是说,如果const声明的变量是一个对象或数组,那么可以对它们的属性或元素进行修改。例如:

js
function foo() { const obj = {a: 1}; obj.a = 2; // 可以修改属性 console.log(obj); // {a: 2} const arr = [1, 2, 3]; arr[0] = 4; // 可以修改元素 console.log(arr); // [4, 2, 3] }

如果想要让对象或数组也不可变,那么可以使用Object.freeze方法来冻结它们。例如:

js
function foo() { const obj = Object.freeze({a: 1}); obj.a = 2; // TypeError: Cannot assign to read only property 'a' of object '#<Object>' }

总结

letconstES6中引入的两种新的变量声明方式,它们与var有以下几个主要区别:

  • letconst属于块级作用域,而var属于函数作用域或全局作用域。
  • letconst不会发生变量提升,而var会发生变量提升。
  • letconst存在暂时性死区,而var不存在暂时性死区。
  • letconst不能重复声明,而var可以重复声明。
  • const是常量,不能重新赋值,而varlet都是可变的。

这些区别都体现了letconst相比var更加优越和现代化的特点,因此建议在编写ES6代码时尽可能使用letconst来代替var

如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:CreatorRay

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!