let
和const
是ES6
中引入的两种新的变量声明方式,它们与var
有一些重要的区别,本质上它们的出现就是为了替代var
,本文将从实现原理的角度来深入探讨let/const
为什么比var
更好。
你可能已经听说过ES6
引入了两种新的声明变量的方式:let
和const
。你可能也知道它们有一些优点,比如变量不会提升、有块级作用域、const
不可修改等。本文将揭开let/const
的秘密,让你深入理解它们的优势。
var
声明的变量属于函数作用域或全局作用域,也就是说,它们在声明所在的函数或全局环境中都是可见的,而不受块级结构(如if
、for
、try
等)的限制。例如:
jsfunction foo() {
var x = 1;
if (true) {
var x = 2; // 同一个变量
console.log(x); // 2
}
console.log(x); // 2
}
let
和const
声明的变量属于块级作用域,也就是说,它们只在声明所在的块级结构中有效,而在外部不可见。例如:
jsfunction foo() {
let x = 1;
if (true) {
let x = 2; // 不同的变量
console.log(x); // 2
}
console.log(x); // 1
}
这样的设计可以避免var
声明的变量污染全局或函数作用域,造成意料之外的结果。同时,也可以让代码更加清晰和模块化,因为每个块级结构都可以有自己的局部变量。
var
声明的变量会发生变量提升(hoisting)
,也就是说,无论在哪里声明,都会被视为在作用域的最开始处声明。这意味着,可以在声明之前使用变量,但是此时变量的值是undefined
。例如:
jsfunction foo() {
console.log(x); // undefined
var x = 1;
}
let
和const
声明的变量不会发生变量提升,也就是说,它们只在声明之后才能使用,否则会报错。例如:
jsfunction foo() {
console.log(x); // ReferenceError: x is not defined
let x = 1;
}
这样的设计可以避免var
声明的变量由于提升而导致的逻辑错误或隐蔽的bug
。同时,也可以强制让代码更加规范和严谨,因为必须先声明后使用变量。
由于let
和const
声明的变量不会发生变量提升,所以在声明之前使用它们会报错。但是这并不意味着它们在声明之前就不存在,事实上,它们已经存在于块级作用域中,只是处于一种不可访问的状态,这就是所谓的暂时性死区(temporal dead zone)
。例如:
jsfunction foo() {
console.log(typeof x); // ReferenceError: x is not defined
let x = 1;
}
如果let
和const
声明的变量在声明之前不存在,那么上面的代码应该输出undefined
,而不是报错。这说明x
已经存在于块级作用域中,但是由于暂时性死区的原因,不能被访问。
暂时性死区的存在可以避免let
和const
声明的变量被意外覆盖或污染。例如:
jsvar x = 1;
function foo() {
if (true) {
// 这里存在暂时性死区
console.log(x); // ReferenceError: x is not defined
let x = 2; // 不会覆盖全局变量x
}
}
如果没有暂时性死区,那么上面的代码会输出1
,而不是报错。这说明let声明的x
并没有覆盖全局变量x,而是在块级作用域中创建了一个新的变量x
。
var
声明的变量可以在同一个作用域中重复声明,后面的声明会覆盖前面的声明。例如:
jsfunction foo() {
var x = 1;
var x = 2; // 重复声明
console.log(x); // 2
}
let
和const
声明的变量不能在同一个作用域中重复声明,否则会报错。例如:
jsfunction foo() {
let x = 1;
let x = 2; // SyntaxError: Identifier 'x' has already been declared
}
这样的设计可以避免var
声明的变量由于重复声明而导致的混乱和错误。同时,也可以保证let
和const
声明的变量的唯一性和稳定性。
var
和let
声明的变量都是可变的,也就是说,可以对它们进行重新赋值。例如:
jsfunction foo() {
var x = 1;
x = 2; // 可以重新赋值
console.log(x); // 2
}
const
声明的变量是常量,也就是说,一旦声明,就不能对它们进行重新赋值。否则会报错。例如:
jsfunction foo() {
const x = 1;
x = 2; // TypeError: Assignment to constant variable.
}
这样的设计可以保证const
声明的变量的不可变性和恒定性,避免了由于重新赋值而导致的逻辑错误或隐蔽的bug
。同时,也可以让代码更加清晰和可读,因为可以一目了然地知道哪些变量是不会改变的。
需要注意的是,const
声明的变量只保证了它们的引用不可变,而不保证它们所指向的值不可变。也就是说,如果const
声明的变量是一个对象或数组,那么可以对它们的属性或元素进行修改。例如:
jsfunction 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
方法来冻结它们。例如:
jsfunction foo() {
const obj = Object.freeze({a: 1});
obj.a = 2; // TypeError: Cannot assign to read only property 'a' of object '#<Object>'
}
let
和const
是ES6
中引入的两种新的变量声明方式,它们与var
有以下几个主要区别:
let
和const
属于块级作用域,而var
属于函数作用域或全局作用域。let
和const
不会发生变量提升,而var
会发生变量提升。let
和const
存在暂时性死区,而var
不存在暂时性死区。let
和const
不能重复声明,而var
可以重复声明。const
是常量,不能重新赋值,而var
和let
都是可变的。这些区别都体现了let
和const
相比var
更加优越和现代化的特点,因此建议在编写ES6
代码时尽可能使用let
和const
来代替var
。
本文作者:CreatorRay
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!