当面试官问我 let、const、var 的区别,竟然直接让我回家?

691 阅读5分钟

2024 年 7 月 9 日,天气晴,气温 39 度。

在这个风和日丽上午,小张接到一个年薪 30w 的前端面试邀请。这是小张失业后的第 30 次面试邀约,本着事不过 30 的原则,小张对此次面试充满信心,于是他顶着炎炎烈日骑上自带加热坐垫功能的小单车来到了面试地点....

开始面试,在一番自我介绍过后,面试官问出了第一个问题:谈一谈 let 和 var 的区别?

小张心里大喜,心想"这么简单的面试题?那这个岗位我还不是手到擒来!",于是小张这样回答:

  • let 只能定义一次,而 var 则可以定义多次
  • let 只在块级作用域生效
  • var 存在变量提升
  • const 一般定义常量,一旦定义无法修改

面试官说:没了? 小张回答:没了...

然后面试官就让小张回去等通知了,于是小张又骑上温暖坐垫小单车回了家。

小张自己心里也清楚这次肯定叕黄了,就因为他的回答太简单,于是他回到家痛定思痛玩了一把亚索之后重新总结了 let、const、var 的区别

正文开始:

块级作用域

let 是 ES6 新增的命令,和 var 差不多都是用来声明变量的,不同的是 let 只在块级作用域生效(块级作用域是指大括号{}中的内容,比如 for 循环,函数或者直接用{}等):

{
  let a = 1;
  var b = 2;
}

a; // ReferenceError: a is not defined.
b; // 2

上面代码可以看到,用 var 声明的 b 变量可以在块级作用域外访问,而用 let 声明的 a 变量则不能访问

实际开发中最常见的 for 循环就是一个很好的例子,如

for (let i = 0; i < 10; i++) {
  // ...
}

console.log(i);
// ReferenceError: i is not defined

很明显在 for 循环中 let 声明的 i 只在循环体内有效,如果我们改成 var 的话

for (var i = 0; i < 10; i++) {
  // ...
}

console.log(i); //10

可以看到 i 最后会被赋值为 10,那么这样会带来一个问题:变量 i 只用来控制循环,但是循环结束后,它并没有消失,泄露成了全局变量。所以在 for 循环中我们最好使用 let 来定义变量

再来看一个 for 循环的经典问题

let arr = [];
for (var i = 0; i < 10; i++) {
  arr[i] = () => {
    console.log(i);
  };
}
arr[0](); //10
arr[1](); //10
//...

可以发现函数执行后结果打印的都是 10,这是因为 i 是 var 声明的,全局生效,循环过后 i 变成了 10,所有调用函数自然打印的就是 10 了。但是如果使用 let 就不一样了

let arr = [];
for (let i = 0; i < 10; i++) {
  arr[i] = () => {
    console.log(i);
  };
}
arr[0](); //0
arr[1](); //1
//...

可以看到最后打印的是 0 和 1,这其实才是我们需要的结果。这是因为 let 声明的变量只在块级作用域生效,每次循环都会重新声明 i,所以每个循环体都是不同的块级作用域,因此执行数组函数的时候调用的是对应块级作用域中的函数

for 循环有一个特殊的地方,在循环变量(括号里的)那部分属于父作用域,循环体中则属于子作用域,因此循环体内可以重新再定义 i 变量而不会报错(let 在同一作用域不可重复定义,但子作用域是可以重复定义父作用域变量的)

for (let i = 0; i < 4; i++) {
  let i = "ddd";
  console.log(i);
}
// ddd
// ddd
// ddd
// ddd

重复声明

let 不允许在同一块级作用域重复声明,var 可以

//报错
function func() {
  let a = 10;
  let a = 1;
}

//不报错
function func() {
  var a = 10;
  var a = 1;
}

变量提升

在 var 中存在反人类的现象,变量可以在声明之前使用,值为 undefined

console.log(a); //undefined
var a = 1;

这会带来很多问题,比如让代码变得难以理解,问题难以追踪,不知道当时设计出来是怎么想的。而 let 就不存在变量提升,如果变量在声明之前使用就会直接报错

console.log(a); //a is not defined
let a = 1;

暂时性死区

暂时性死区是指,当一个块级作用域使用了 let 声明一个变量,那么这个变量就和外部无关了,不再受外部影响

var a = 1;
{
  a = 2; //ReferenceError
  let a;
}

可以看到我们在块级作用域用 let 定义了 a 变量,所以作用域内部的 a 就和外部无关了,根据上面提到的,let 不存在变量提升,所以会报错

const

const 声明的是一个只读的常量,一旦声明,这个常量就不可改变

const a = 1;
a = 2; //TypeError: Assignment to constant variable.

const 其它用法如作用域,暂时性死区,变量提升等和 let 用法一致

对于 const 有一个非常值得注意的点是:const 只能保证它所定义的值的地址指向是不变的即指针是固定的。倘若这个指针指向的是一个引用类型如 object 类型,那么这个值的变化就不是它所能控制的了

const obj = {};
//obj添加属性不会报错
obj.a = 1;
obj.b = 2;
console.log(obj); //{a:1,b:2}

//直接修改obj指向,报错
obj = {}; //TypeError: Assignment to constant variable

到这里小张就总结完了它们的所有区别,相信他下次面试一定能成功~