和面试官唠嗑你需要懂“举一反三” (以闭包为例) 🔥🔥

2,497 阅读12分钟

星光不问赶路人、岁月不负有心人

前言

前几天去面试真的是被上了一课,下面就以闭包为例,说一下整个面试的过程。

事情经过

事情的的经过就直接还原现场了,方便大家以第一人称的视角去体验一场关于闭包的面试。

说明:因为前段时间北京疫情比较严重,所以基本都是约的线上面试,因此这场面试也是高端大气上档次的腾讯面试会议。下面就说一下从我进入会议室到我出来经历了写什么。

进入会议室——>

我:您好!(既是打招呼也是确认能不能听到彼此说话)。

面试官:你好。(很平淡)

我:面试官您好,我是本场面试的应聘者,我叫NDZ。求职的是贵公司的高级前端开发工程师。

面试官:你好,那先自我介绍一下。(依旧很平淡......)

旁白:我心想你不是拿着我的简历的么?为什么还要自我介绍?再说我刚刚不是介绍了么:我叫啥,应聘的是什么岗位。为什么还要自我介绍?但转念一想我觉得他应该想问的是我的工作经历。然后我不确定的又自我介绍了一下。

我:面试官您好,我是本场面试的应聘者,我叫NDZ。求职的是贵公司的高级前端开发工程师。我16年开始接触前端,到现在已经6年的工作经验了。16年-17年做的是h5动画相关的工作, 17年-18年是一名前端讲师,18年-21年.....

旁白:我整整666个字把我的工作经历说了一遍,还介绍了几个我觉得拿得出手的项目

面试官:行,那咱们开始今天的面试吧,我这边就问两道题,一道答上来就行,另一道需要共享屏幕写一下,能接受么?(说的超级平淡!!!!!)

旁白:看他平淡的话语我开始紧张了,我觉得这一定是个不是对付的面试官。并且就问一个问题,写一个问题,我心想这题绝逼难,怀着忐忑的心情我回答到

我:能接受,您问吧。

面试官:好的,那你说一下你理解的闭包。

谈谈你对闭包的理解

旁白:直接懵逼!!!!闭包?就问一个题然而问我闭包?我瞬间感觉这是缘分到了啊!!!!哈哈哈哈哈哈哈哈哈哈哈哈哈...... 瞬间我就坐直了,心情一片大好回答到

我:闭包就是能够读取其他函数内部变量的函数,可以理解为“定义在一个函数内部的函数”。本质上,闭包是将函数内部和函数外部连接起来的桥梁。

旁白:说完以后我开心的一批,心想:稳了!!

面试官:没了?

旁白:就这两字让我紧张了,真的紧张!心想不对么?不能啊!难道还想听点别的?于是我又说

我:闭包有它的优缺点,优点是:

闭包的优点

  1. 可以访问到函数内部的局部变量

  2. 可以避免全局变量被污染

闭包的缺点

  1. 层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大。因此不能滥用闭包,否则会造成网页的性能问题。IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

  2. 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

旁白:为了避免面试官再说:没了?我不仅说了缺点还说了如何避免,这下我觉得应该稳了。

......

旁白:然后在我觉得稳了以后,看向面试官,他也看向我,过了短暂的几秒,我看我确实不说话了,于是:

面试官:没了?

旁白:瞬间头大!还有啥?闭包的概念,优缺点都说了还有啥?

我:还有么?我不记得还有别的什么优缺点啊?

面试官:嗯.....(他好像有点无语,然后说道)闭包不用会有优缺点么?

我:不会啊!(瞬间醒悟,应还没说)哦!还有闭包的应用。(尴尬一笑)闭包的应用比较多,平常时间在项目中经常用到的地方有:

闭包的应用

自执行函数

立即执行函数就是一个闭包,因为立即执行函数里面的任何一个函数都可以使用它的i这个变量

for (var i = 0; i < list.length; i++) {
// 利用for循环创建了4个立即执行函数,每个立即执行函数都是一个闭包
(function(i) {
    list[i].onclick = function() {
      console.log(i);
    }
 })(i);
}

函数柯里化

所谓"柯里化",就是把一个多参数的函数,转化为单参数函数。
也可理解为就是一种函数的转换,可将一个函数从可调用的f(a, b, c)转换为可调用的 f(a)(b)(c)。举个例子:


// 柯里化之前
function add(x, y) {
  return x + y;
}
add(1, 2) // 3

// 柯里化之后
function addX(y) {
  return function (x) {
    return x + y;
  };
}
addX(2)(1) // 3

防抖和节流

首先说一下什么是防抖和节流

  • 防抖:从字面意思就能看出来其实就是防止抖动,说明白一点就是,为一个事件设置一个时间N,N秒后再执行该事件,若在N秒内被重复触发,则重新计时。
  • 节流:他和防抖一样都需要设置一个时间N,不同的是N秒内只运行一次,若在N秒内重复触发,则重复触发的不生效。

防抖和节流都是工作中经常会用到,平常到我们每天几乎都会使用,举个例子:浏览器的搜索功能,比如百度的搜索就应用了防抖,因为他不可能每次变化都去做一次查询,以百度的流量来看,那请求不得在现在的基础上翻好几倍,那服务器的压力也太大了。节流就更常见了,比如页面滚动到页面底部时会出现一个回到顶部的按钮,用的就是节流,当我们向下滚动页面的时候,onscroll事件触发的频率太高太高,稍微向下滚动一点,就已经触发了很多次,而且其中很多的触发并没有实在的意义,如何减少触发的频率,减少那么多的计算操作就可以用防抖。

举例说一下:

// 防抖
// func是用户传入需要防抖的函数
// wait是等待时间

// 如果用户调用该函数的间隔小于wait的情况下,上一次的时间还未到就被清除了,并不会执行函数
function debounce(func, wait) {
    var timer=null;
    return function() {
        //保存当前调用的dom对象
        var _this=this;
        //保存事件对象
        var args=arguments;
        clearTimeout(timer)
        timer=setTimeout(function() {
            func.apply(_this, args)
        }, wait)
    }
}


// 节流
function throttle(func, wait) {
    var timer=null;
    return function() {
        var _this=this;
        var args=arguments;
        if(!timer) {
            timer=setTimeout(function() {
                timer=null;
                func.apply(_this, args)
            }, wait)
        }
    }
}

旁白:用了整整6分66秒我才把常用的几种应用说完,然后我看向面试官。

面试官:没了?

旁白:我草,我不面了,我走行了吧???

我:嗯......没了。

面试官:你刚才说到闭包的缺点的时候,你说滥用闭包会造成内存泄漏,那你可以补充一下为什么会造成内存泄漏吗?

旁白:这个“补充”用的很好呀,说好一个问题就是一个问题,这是补充,不是第二个问题。呵呵哒!

我:为什么会造成内存泄漏?因为滥用闭包啊!

面试官:我是说造成内存泄漏的机制。(不再那么平静了,有点对我无语的感觉了)

我:你是说垃圾回收机制么?

面试官:嗯......是的。

我:(于是我开始解释垃圾回收机制)

垃圾回收机制

在说垃圾回收机制之前我们需要先了解一下内存泄露和内存溢出

内存泄露

占用的内存没有被及时释放,就会造成内存泄漏。

内存溢出

当程序运行时需要的内存超出了剩余的内存这就属于内存溢出。而内存泄漏积累多了就会导致内存溢出

那么,内存溢出是因为内存泄露而导致的,那 内存泄漏是什么导致的呢?那就是垃圾回收机制。JavaScript是在创建变量(对象,字符串等)时自动进行了内存分配,并且在不使用它们时“自动”释放,而这个释放的过程就被称为垃圾回收。

垃圾回收机制的回收算法主要是引用和标记

引用

垃圾回收算法主要依赖于引用的概念。在内存管理的环境中,一个对象如果有访问另一个对象的权限(隐式或者显式),叫做一个对象引用另一个对象。例如,一个 Javascript 对象具有对它原型的引用(隐式引用)和对它属性的引用(显式引用)。引用的策略叫做引用计数垃圾收集

引用计数法

引用计数法是最初级的垃圾收集算法。此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”。如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收。 举个例子:


// 创建两个对象,一个对象作为属性被另一个对象引用,另一个对象则被分配给变量obj
var obj = {
  a: {
    b:2
  }
};
// 到此,没有一个可以被垃圾收集

// obj2 变量是第二个对“另一个对象”的引用
var obj2 = obj; 

// 为obj赋值为1
obj = 1; 

// 到此,“另一个对象”的原始引用 obj 已经没有 现在“另一个对象”只有一个obj2变量的引用了,

// 创建变量obj_a引用“另一个对象”的a属性
var obj_a = obj2.a; 

// 到此,“另一个对象”有两个引用了,一个是 obj2,一个是 obj_a

// 为变量obj2赋值为‘ndz’
obj2 = "ndz"; 

// 到此,“另一个对象”虽然已经是零引用了,但是它的属性a却还在被obj_a引用,所以还不能回收

// 把obj_a赋值为null
obj_a = null; 

// 到此,a属性现在也是零引用了,那么“另一个对象”可以被垃圾回收了

标记清除算法

标记清除算法把“对象是否不再需要”简化定义为“对象是否可以获得”。
假定设置一个叫做根(root)的对象(在 Javascript 里,根是全局对象)。垃圾回收器将定期从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象,就这样一层一层找下去,一直找到所有可以获得的对象和收集所有不能获得的对象。到此不能获得的对象就是不再需要的。

从 2012 年起,所有现代浏览器都使用了标记清除算法。所有对 JavaScript 垃圾回收算法的改进都是基于标记清除算法的改进。

我:我的回答完了

面试官:行,那咱们进行手写代码题,你先打开编码工具然后分享一下桌面

旁白:于是我打开了VSCode,然后共享了桌面

面试官:两题选一题,一个是贪吃蛇一个是俄罗斯方块,能写多少写多少。

我:好的,我选贪吃蛇(因为我写过很多次了)

旁白:于是我耗时30分钟左右写完了我简易版的贪吃蛇。

面试官:好的,那今天我这的面试就到这里了,你有什么想向我了解的么?

我:我想问一下本次面试能通过么?(我超级直接的)

面试官:(尴尬一笑以后说)这个我们后期会做一个对比,如果通过的话Hr会和你约二面的。

我:好的,那我没啥想问的了。

面试到此结束。。。。。

但文章还没结束!!!!!

这里咱们复盘一下,首先说一下当面试官让你说说你对闭包的理解的时候,我们应该说的比较全面,不是就说一个概念就完事了,我觉得这么回答会比较好:

谈谈你对闭包的理解

  • 首先说概念
  • 其次说优缺点
  • 然后说一下缺点为什么造成的,能怎么改进
  • 最后是应用,在项目中咱们都在哪些地方用到了

最后就是通过这次面试总结一下该怎么面试(此处仅代表个人观点啊,不喜勿喷!)

  • 面试最忌讳问啥说啥,当面试官问一个问题时,我们在尽可能完整的回答完以后再尽量的去补充问题所涉及到的知识点,但切记不要扯开话题。
  • 其次在补充回答的时候可以尽量的往自己擅长的方向去靠,这样有利于接下来面试官问你所擅长的领域,但也切记硬靠。
  • 最后就是当面试官问你有啥想了解的,你可以先扪心自问一下,这场面试面的咋样,如果面的挺好,那就可以问点你挑团队的问题,比如研发团队多少人,用的什么技术,做的啥项目之类的。别啥也不问到时候手握10个offer的你选一个就你一个前端的团队,技术用的还是JQuery。当然如果面的不咋滴,人间眼瞅着就是随口一说那就别问了,省的相看两厌。

行吧,到此本篇文章结束,希望对你有帮助。

我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿