在JavaScript中,有三种不同的方式来声明变量:const、let和var。从历史上看,var是声明JavaScript变量的唯一方式:
var name = 'Robin Wieruch';
确切地说,是对JavaScript的一个补充。2015年的JavaScript ES6--使const 和let ,这也是该语言可用的:
let firstName = 'Robin';
const lastName = 'Wieruch';
显然,在JavaScript中声明和定义变量的方式有了更多的选择,这对于刚接触该语言的开发者来说并不容易。但是,我们可以快速地让初学者更轻松。可以说const和let取代了var,而var在现代的JavaScript中已经不再使用。这主要是因为两个原因。
- const和let的用法和意图更加明确
- var有它的怪癖,这些怪癖已经被const和let解决了。
let与var
我能想到的let(和const)优于var的原因有两个:提升和范围。让我们来看看下面的示例代码,用var作为首选的变量声明来计算一个数组中人的年龄:
var personOne = { name: 'Robin Wieruch', age: 30,};
var personTwo = { name: 'Liesa Huppertz', age: 29,};
var family = [personOne, personTwo];
function sumAge(persons) { var sum = 0;
for (var i = 0; i < persons.length; i++) { sum = sum + persons[i].age; }
return sum;}
var result = sumAge(family);
console.log(result);// 59
首先,用var声明的每个变量都会被默认初始化为undefined,即使它在实际代码中还没有被声明/定义:
function sumAge(persons) { console.log(sum); // undefined
var sum = 0;
for (var i = 0; i < persons.length; i++) { sum = sum + persons[i].age; }
return sum;}
如果我们的变量最终没有在代码中声明,一旦我们的JavaScript代码执行,我们就会得到一个 "ReferenceError: sum is not defined",这可能已经是我们想要的行为。然而,由于sum在代码中的某个地方被声明/定义了,所以JavaScript预防性地将其初始化为未定义。
但在阅读代码时,这似乎并不正确,因为人们会认为,一旦变量被实际声明或定义,它就被声明或定义了。通过使用let而不是var,我们避免了这种行为,并且无论如何都会得到想要的 "ReferenceError"。
function sumAge(persons) { console.log(sum); // ReferenceError: sum is not defined
let sum = 0;
for (var i = 0; i < persons.length; i++) { sum = sum + persons[i].age; }
return sum;}
第二,在用var声明JavaScript变量时,范围是另一个问题。 让我们再来看看前面的例子,但输出另一个var定义的变量。
function sumAge(persons) { let sum = 0;
for (var i = 0; i < persons.length; i++) { sum = sum + persons[i].age; }
console.log(i); // 2
return sum;}
由于var在JavaScript中是函数作用域,我们的for-loop的迭代器可以在for-loop(块)之外访问。与var相反,let是块作用域,这意味着它只能在for-loop这样的块中定义。
function sumAge(persons) { let sum = 0;
for (let i = 0; i < persons.length; i++) { sum = sum + persons[i].age; }
console.log(i); // ReferenceError: i is not defined
return sum;}
同样,与var相比,这是let(和const)建立的更理想的行为。让let(和const)成为block-scoped,有助于我们像之前的hoisting行为一样,推理我们的代码。如果不使用var,作为JS开发者,我们只需使用const和let就可以了。
练习。
- 哪些JavaScript变量的声明是块作用域或函数作用域?
- 哪些JavaScript变量的声明是悬挂式的?
const与let
那么,如果在JS中主要只使用const和let,因为用var声明的变量会有范围问题,那么const和let之间有什么主要区别呢?因为两者都是区块范围内的,不存在范围问题,唯一的区别是
- 用 const 声明的变量不能被重新赋值
- 用let声明的变量可以被重新赋值。
let firstName = 'Robin';firstName = 'Dennis';// does work
const lastName = 'Wieruch';lastName = 'Huppertz';// doesn't work
有些人可能会立即得出结论,用const声明的JavaScript变量一定是不可变的(unchangeable),因为它不能被变异(change)。然而,重要的是要知道,如果const被用于一个JavaScript对象或数组,它的内部属性仍然可以被重新赋值。
const person = { firstName: 'Robin', lastName: 'Wieruch',};
person = { firstName: 'Thomas', lastName: 'Wieruch',};// doesn't work
person.firstName = 'Dennis';// does work
console.log(person.firstName);// "Dennis"
这就是为什么把JavaScript的const变量声明与不可变的数据结构混为一谈不是个好主意。
例如,const给了我们一个强烈的信号--但只是一个信号,因为正如我们在前面的例子中所看到的,它并不是对所有的JavaScript数据类型都强制执行的--表明这个变量的目的(!)是不可变的。如果有人想表明JavaScript是可变的,那么在变量声明中就应该使用let而不是const。
let personOne = { firstName: 'Robin', lastName: 'Wieruch',};// Saying: "Hey, it's okay to change this variable eventually."
let personTwo = { firstName: 'Liesa', lastName: 'Huppertz',};// Saying: "Hey, it's okay to change this variable eventually."
function marryHerToHim(her, him) { her.lastName = him.lastName;}
marryHerToHim(personTwo, personOne);
console.log(personTwo);// { firstName: 'Liesa', lastName: 'Wieruch' }
然而,由于在JavaScript中保持数据结构的不可变性是一种常见的做法,前面的例子最好是不改变其中的一个对象,而是返回一个新的对象来表达所需的变化。因此,使用const而不是let将表明这一意图,但如前所述,并不强制执行。
const personOne = { firstName: 'Robin', lastName: 'Wieruch',};// Saying: "Hey, don't change this variable over its lifetime."
const personTwo = { firstName: 'Liesa', lastName: 'Huppertz',};// Saying: "Hey, don't change this variable over its lifetime."
function marryHerToHim(her, him) { return { firstName: her.firstName, lastName: him.lastName, };}// Saying: "Instead return a new variable if the variable has to change."
const marriedPersonTwo = marryHerToHim(personTwo, personOne);
console.log(marriedPersonTwo);// {// firstName: "Liesa",// lastName: "Wieruch",// }
那么,如果const是最常用的,因为它没有给你重新赋值变量的能力,那么什么时候应该使用let而不是const呢?通常情况下,let被用于变量必须改变的操作中。
const personOne = { name: 'Robin Wieruch', age: 30,};
const personTwo = { name: 'Liesa Huppertz', age: 29,};
const family = [personOne, personTwo];
function sumAge(persons) { let sum = 0;
for (let i = 0; i < persons.length; i++) { sum = sum + persons[i].age; }
return sum;}
const result = sumAge(family);
console.log(result);// 59
在for-loop中,通常会看到let用于迭代变量。另外,如果一个变量随着时间的推移而发生变化,这里的年龄之和就是这种情况,它必须被定义为let,否则我们会遇到一个未发现的类型错误。对常量变量的赋值。-异常。
function sumAge(persons) { const sum = 0;
for (let i = 0; i < persons.length; i++) { sum = sum + persons[i].age; // doesn't work }
return sum;}
const result = sumAge(family);
// Uncaught TypeError: Assignment to constant variable.
所以我们可以看到,在JavaScript中使用const和let之间有明确的界限。如果我们想通过不重新赋值来保持不可变的数据结构,并避免重新赋值其内部属性,那么const给了我们和代码库中的其他人一个相当强烈的信号,即不要改变变量。相反,如果我们想改变一个变量,在像for-loops这样的波动性操作中,我们可以使用let而不是const。
作为一个经验法则,我的建议是:
- 避免使用var,因为它在范围/hoisting方面存在奇怪的问题。
- 使用const作为默认值(信号变量不应该改变)。
- 当变量需要被重新分配时,使用let