面试官:你连js闭包都不知道,怎么写代码

62 阅读4分钟
几年前找工作时参加了一场面试,受到很痛的打击,到现在依然记忆犹新,太深刻了。

我那会找工作时已经写了两年的前端了,说实话,写了那么久的前端,没有怎么系统地学习过相关的理论知识。
有些专业术语根本不知道怎么喊,平时有什么业务都是直接开干,面试的时候也没有刷面试题。
于是面试官的一句话给了我重重一击。

上一家公司的经历

上一家公司待了2年,每个月都拖欠工资。不知道是效益不好,还是真的不想发。

  • 公司不大,20来个人。
  • 我们开发前后端加起来5个人,主管不怎么写代码,真正干活的也就我们4个“牛马”。
  • 平时老板业务好像挺忙的,看上去效益并没有不好。
  • 说好的每个月15号发工资,但每次都拖到二十几号。

随着时间的拉长,拖的时间越来越久,最久的一次是三个月才发一次工资
没有人吭声,我觉得不对劲,于是我主动提出了辞职。

好在我走后工资有给我结清。
可后来听到老同事说:公司解散了,而且工资都没有发完。

面试经历

我离职后第一件事就是改简历、投简历,面试题都没刷,咔咔接到好几个面试邀请。
于是我直接去面试了。

根据面试官的介绍,这家公司是做蓝牙耳机的,边上也能看到各种各样的蓝牙耳机放在架子上。

开始面试后,我先做了自我介绍,然后就是和面试官“斗智斗勇”了。

让我记忆深刻的一个问题

面试官问:

“JS闭包是什么?”

我的回答是:

“在一个函数里面定义另一个函数,并且返回这个函数,就是闭包。”

他立刻回复我:

“你确定这就是闭包?JS闭包都不知道,平时怎么写代码?”

听到这句话时,我心里非常不舒服。
当时在想:即使我没有答好,但也不至于写不了代码吧!

于是我解释了一下:

“平时没怎么看理论知识,有时候虽然有这么写过,但不知道该怎么说出来。”

面试官又说:

“那也不应该连闭包这么简单的东西都不知道吧。”

于是我们两人客套几句后,我就走了。

对我来说,这次受的打击有点沉重,但必须得清晰地认识到:确实是自己的知识不足
所以回去后我狠狠地刷面试题,也下定决心:有些技术知识,必须掌握。

为此还是再次介绍一下闭包吧!

“闭包是指在一个函数内部定义了另一个函数,并且这个内部函数引用了外部函数的变量或参数。即使外部函数执行完毕,内部函数仍然可以访问和操作那些外部变量。返回这个内部函数,是使用闭包最常见的方式。”

function createCounter() {
  // 外部函数的变量 `count`
  // 这个变量对于每个创建的计数器都是“私有”的
  let count = 0; 

  // 内部函数(闭包)
  // 它引用了外部变量 `count`
  function counter() {
    count += 1; // 操作外部变量
    return count;
  }

  // 返回内部函数
  return counter;
}

// 使用闭包
// 创建第一个独立的计数器实例
const counter1 = createCounter();
// 创建第二个独立的计数器实例
const counter2 = createCounter();

console.log(counter1()); // 输出: 1
console.log(counter1()); // 输出: 2
console.log(counter1()); // 输出: 3

console.log(counter2()); // 输出: 1 (独立的计数环境!)
console.log(counter2()); // 输出: 2

console.log(counter1()); // 输出: 4 (counter1 的环境依然被保持着)

再看一个非常经典的面试题,很多新手容易被坑到的地方

有问题的代码:

for (var i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i); // 输出什么?
  }, 100);
}
// 输出:3, 3, 3 (而不是预期的 0, 1, 2)

问题原因:setTimeout的回调函数是一个闭包,它引用了同一个变量i。当循环结束时,i的值已经是3,所以所有回调函数都打印3。

上面的闭包修复:

for (var i = 0; i < 3; i++) {
  (function(j) { // 立即执行函数创建一个新的作用域,参数 j 捕获当前 i 的值
    setTimeout(function() {
      console.log(j); // j 是来自闭包环境的变量
    }, 100);
  })(i); // 将当前的 i 值传入
}
// 输出:0, 1, 2

let也可以避开上面的问题

for (let i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i);
  }, 100);
}
// 输出:0, 1, 2

ok了