news 2026/8/29 21:54:00

前端JavaScript面试通关:闭包、原型链与事件循环核心原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
前端JavaScript面试通关:闭包、原型链与事件循环核心原理

金九银十刚过,紧接着就是“铜九铁十”的补录和春招预备期。这段时间我密集地面了几十位前端候选人,从简历筛选到技术面到终面,一个很明显的感受是:JavaScript基础这块,太多人停留在“背得出来、讲不清楚”的阶段。闭包能背出“函数嵌套函数”的不在少数,但被问到“闭包在Vue源码里怎么用的”“闭包和内存泄漏到底是什么关系”就卡住了;原型链能画出图,但解释不了“为什么 es6 class 是语法糖”。说白了,八股文本身没毛病,问题是大多数人只背了“what”,没有打通“why”,一到追问环节就露馅。

这篇文章不打算给你罗列几千道题,而是把我在面试里最常问、也最常看到候选人翻车的 JavaScript 核心考点做一个系统性的梳理。每个主题都会从“面试官到底想考什么”出发,把原理讲透,再配上实际面试中的问法、答法和追问方向。适合正在准备秋招补录、春招,以及工作两年内想跳槽的前端开发。文章会有点长,但每一节都值得你反复看几遍。

1. 数据类型与类型转换:看似送分,实则暗藏三个连环坑

1.1 原始类型和引用类型的本质差异

JavaScript 的数据类型是面试第一题的高频区,但它远没有看起来那么简单。面试官问“JavaScript 有几种数据类型”,很多人能答出 string、number、boolean、null、undefined、object、symbol,再补一个 es2020 的 bigint,算过关。但真正的分水岭在下一句:“原始类型和引用类型有什么区别?”

答案是存储方式不同。原始类型存在栈内存里,值直接存储在变量访问的位置;引用类型存的是堆内存地址,变量里放的是指向堆内存的指针。这个差异引出了一连串行为差异:赋值行为、比较行为、函数传参行为。你写let a = 1; let b = a; b = 2,a 还是 1;但let obj1 = {x: 1}; let obj2 = obj1; obj2.x = 2,obj1.x 也变成了 2。这就是“传值”和“传共享引用”的区别。

这里我建议你记住一个更准确的说法:JavaScript 函数传参永远是按值传递,只不过当参数是引用类型时,这个“值”是地址的拷贝。所以函数内部重新给参数赋值不会影响外部变量,但修改参数对象的属性会影响外部对象。这个细节面试官特别喜欢追问,答得准确能让对方刮目相看。

1.2 typeof、instanceof、Object.prototype.toString.call 三件套

类型判断是面试必考,考察的是你知不知道不同判断方式各自的边界。typeof的坑有两个:typeof null === 'object',以及typeof 函数 === 'function'但函数其实不是类型系统中的一种独立类型。typeof判断不了数组、日期、正则这些对象的具体类型,这时候要用instanceof

instanceof也有坑。它的原理是检查构造函数的 prototype 是否出现在对象的原型链上,所以跨 iframe 场景下,不同全局执行环境的 Array 构造函数不一样,arr instanceof iframe.contentWindow.Array会返回 false。这也是为什么很多库内部判断数组不用instanceof而用Array.isArray,因为后者是通过 ECMAScript 规范定义的内部槽位来判断的,跨环境依然准确。

真正能达到“通杀”效果的是Object.prototype.toString.call。它内部会读取对象的Symbol.toStringTag属性,然后返回[object Type]格式的字符串。Object.prototype.toString.call([])返回[object Array]call(/x/)返回[object RegExp]call(null)返回[object Null]。面试时如果能从“为什么Object.prototype.toString.calltypeof更准确”这个角度回答,面试官会看到你对语言规范的理解深度。

1.3 == 和 === 的转换规则:能画出去面试官就服你

==的隐式转换规则是很多人的噩梦,但它其实有非常清晰的优先级。两个类型不同时:先看有没有 boolean,有就转成 number;再看有没有 string,有就 string 转 number;然后看有没有对象,有就调用对象的 valueOf 或 toString 转原始值;最后两边都是 number 就比数值。null 和 undefined 相互==为 true,但和其他任何值都不相等。

我建议你把这个规则简化成一句面试话术:“==的转换优先级是 boolean 转 number,string 转 number,对象转原始值,最终都是往 number 收敛。唯一例外是 null 和 undefined 的比较。” 这样讲既准确又显得思路清晰。手写一个基础版:1 == '1'是 true(string 转 number),[] == 0是 true(对象转原始值得到空字符串,再转成 0),[1,2] == '1,2'是 true(数组 toString 后是 '1,2')。这些例子建议自己动手在控制台跑一遍,印象会深很多。

2. 函数与 this 指向:面试官最爱的一题,也是最容易暴露水平的一题

2.1 this 的四种绑定规则,以及箭头函数的特殊性

this 指向是 JavaScript 面试的必考重灾区。很多候选人能说出“谁调用指向谁”,但一遇到复杂场景就蒙。面试时我通常会给一个综合代码题,让你判断多层嵌套下的 this 指向,然后追问“为什么”。要答好,必须掌握四条绑定规则,按优先级排列:new 绑定、显式绑定(call/apply/bind)、隐式绑定(对象调用)、默认绑定(独立函数调用)。

先说默认绑定。非严格模式下,独立函数调用的 this 指向全局对象(浏览器里是 window),严格模式下是 undefined。隐式绑定看的是调用点:obj.fn()中 fn 的 this 指向 obj,但const fn = obj.fn; fn()就变成默认绑定了,因为调用点丢失了对象上下文。显式绑定就是 call/apply/bind 强制指定 this。new 绑定优先级最高:new 一个函数时,this 指向新创建的对象。

箭头函数是唯一的例外:它没有自己的 this,this 在定义时从外层作用域捕获。注意“定义时捕获”和“调用时确定”的区别。我习惯用一个对比题让候选人理解:对象方法里套一个箭头函数,和套一个普通函数,分别输出什么。前者输出对象(箭头函数捕获外层的 this),后者输出 window 或 undefined。

2.2 call、apply、bind 的实现原理与手写题

面试官让你手写 call,其实考的是“你怎么用 JavaScript 模拟一个不存在的机制”。答案的核心思路是利用对象的属性调用:把函数赋值给目标对象的一个临时属性,然后通过目标对象调用它,这样隐式绑定规则就会把 this 指向目标对象。

我当年面试时写过一版,现在依然觉得这是最清晰的写法:给传入的 context(没有就默认全局)挂一个 Symbol 属性,把这个属性值设为当前函数,然后context[fn](...args)调用,调用完 delete 掉这个临时属性。用 Symbol 是为了避免覆盖对象上已有的同名属性。apply 和 call 的区别仅仅是参数形式,一个传数组,一个展开传参。bind 稍微复杂一点,它返回一个新函数,新函数内部用apply固定 this,同时要支持参数拼接和 new 调用。实现 bind 时有个容易忽略的点:如果返回的函数被 new 了,this 应该指向新创建的实例,而不是之前 bind 绑定的对象,同时还需要把原型关系接上。这部分能写到什么程度,基本能反映候选人对 this 机制的真实理解水平。

2.3 这道经典面试题的完整推演过程

我拿一道出现频率极高的题来完整推演一遍,你感受一下正规的分析路径。有这样一个对象和方法,然后单独取出来调用:

var name = 'global'; var obj = { name: 'obj', getName: function() { console.log(this.name); } }; var fn = obj.getName; fn();

输出是'global'。原因很简单:fn = obj.getName这一步把函数引用取出来了,调用点变成了独立的变量调用,隐式绑定失效,走默认绑定规则,非严格模式下 this 指向全局对象。如果面试官再追问“怎么让它输出 obj”,那就是加一层显式绑定:fn.call(obj)或者用 bind 返回新函数。如果把这个函数改成箭头函数,答案又会不同,因为箭头函数在定义时捕获了所在作用域的 this,而对象字面量定义时所在的 this 是全局对象,所以输出还是'global'。这一步很多候选人容易答错,建议重点记忆。

3. 闭包与作用域链:从背概念到讲出场面的跨越

3.1 闭包的本质是“函数 + 词法作用域的组合”

闭包这个问题,面试官问出来的时候,其实想听的远不止“函数嵌套函数”。真正有深度的答案是:闭包是指一个函数能够访问它定义时所在作用域中的变量,即使这个作用域已经执行完毕。关键在“定义时”而不是“调用时”,这与 JavaScript 的词法作用域(lexical scope)机制绑定在一起。

JavaScript 引擎在函数创建时会保存一个[[Environment]]内部槽位,指向当前词法环境。函数执行时,如果变量在当前作用域找不到,就沿着外层词法环境的链表逐级查找,这条链就是作用域链。闭包之所以能“记住”外部变量,本质是因为函数对象引用了外部词法环境,外部环境不能被垃圾回收,变量因此存活下来。

面试中被追问的场景通常是:for 循环里用 var 定义变量,setTimeout 里打印 i,输出全是最后一次循环的值。原因就是 var 是函数作用域,循环体没有独立词法环境,所有 setTimeout 回调共享同一个变量 i。解决办法有三个。第一个是 var + 立即执行函数,var 是函数作用域,立即执行函数每个循环都生成一个新作用域,参数 i 被锁定。第二个是 let,let 是块级作用域,每次迭代都会创建一个新的词法环境并绑定当前的 i。第三个是用 bind 或者用函数的默认参数,本质也是创建一个新的绑定。这三个方案都能答出来的候选人,在闭包这个知识点上基本不会翻车。

3.2 闭包的实际应用:不只是“返回值里套函数”

很多候选人能默写闭包的例子,但被问到“项目里哪里用到过闭包”就答不上来。这是很典型的基础不牢表现。闭包在真实前端项目里的应用场景太多了,我帮你梳理几个最容易讲清楚的。

场景一是防抖和节流的实现。防抖函数内部用闭包保存一个 timer 变量,每次事件触发时检查 timer 是否存在,存在就 clearTimeout 重新计时。timer 这个变量对每次调用防抖返回的新函数都是独立的,这就是闭包“私有变量”能力的直接应用。场景二是模块化开发里的私有变量。比如一个计数器模块,内部变量 count 只希望通过返回的方法来修改,外部无法直接访问,这就是闭包模拟私有属性。场景三是函数工厂:比如一个 createGreeting 函数,接收一个问候语前缀,返回一个拼接名字的函数,前缀通过闭包被记住。

如果你用 Vue 或 React,还可以这样讲:组合式 API 里的 setup 或 hook 每次执行都会创建新的闭包作用域,组件状态就保存在闭包变量中;React 的 function component 每次渲染都是一个新的闭包,这也解释了为什么 useEffect 里拿到的 state 是“旧值”的问题。能讲到这里,说明你不仅仅会背定义,还能把机制和平时的开发经验串起来。

3.3 闭包与内存泄漏:面试官问这个,是想看你有没有踩过真实的坑

闭包会带来内存回收的问题吗?严格说,闭包本身不会必然导致内存泄漏,但闭包引用的词法环境如果长期无法被释放,就会造成内存增长。面试官问这个点时,真正想听的是你对“什么对象该被回收”的理解。

举个具体例子:一个大型数组被一个闭包引用,而闭包又被一个全局变量长期持有,那这个数组永远无法被垃圾回收器回收。在低端移动设备上,这样的泄漏会导致页面越来越卡。另一个常见泄漏场景是事件监听器。一个组件销毁时,如果事件监听函数还挂在全局对象或另一个长生命周期对象上,而监听函数内部又闭包引用了组件数据,整个组件的数据都无法释放。

面试时的标准答案是:闭包可能导致内存泄漏,原因是闭包持有外部词法环境的引用,GC 无法回收;解决方案是在闭包不再需要时置为 null,或者避免在长生命周期对象上注册持有短生命周期数据的函数。再补充一句“现代浏览器如果检测到词法环境中的变量在闭包内没有被使用,会进行优化,只保留被使用的变量”,就能体现出你关注过 V8 的优化策略。

4. 原型、原型链与继承:JavaScript 面向对象的底层逻辑

4.1 prototype、__proto__ 和 constructor:三个最容易混淆的属性

原型这块,候选人最常见的状态是“我背过,但画图就乱”。其实只需要理清三个主角:每个函数都有 prototype 属性,它指向一个对象,这个对象是 new 该函数创建出来的实例的“模板”;每个对象都有__proto__属性,它指向该对象“构造它”的函数的 prototype。也就是说,new Person()出来的实例 p,它的p.__proto__ === Person.prototype。constructor 是 prototype 对象上的一个属性,指回构造函数本身,即Person.prototype.constructor === Person

为什么需要原型链?因为当访问一个对象的属性时,如果对象自身没有,JavaScript 会沿着__proto__找它构造函数的 prototype,然后再沿着 prototype 的__proto__继续往上,直到顶端的Object.prototype,再往上就是 null 了。这套机制实现了属性继承和方法共享。一个很经典的面试题是:p.toString()为什么能调用成功?因为沿着原型链找到了Object.prototype上的 toString。而p.hasOwnProperty('toString')返回 false,因为这是继承来的属性。

4.2 手写一个标准的继承链:寄生组合式继承

继承是原型考点里最能拉开差距的题目。几种继承方式的优劣,我直接给结论。原型链继承(让子类 prototype 指向父类实例)的缺点是实例共享引用属性。构造函数继承(在子类构造函数里调用Parent.call(this))解决了共享问题,但无法继承原型上的方法。组合继承(构造函数 + 原型链)兼具两者,但会调用两次父类构造函数。最优解是寄生组合式继承:核心是用一个临时构造函数来转换原型指向,避免调用父类构造函数。

你可以在面试现场直接这么写:

function inherit(Child, Parent) { var proto = Object.create(Parent.prototype); proto.constructor = Child; Child.prototype = proto; }

Object.create(Parent.prototype)会创建一个新对象,它的__proto__指向 Parent.prototype,然后手动把 constructor 指回 Child。这样做既建立了正确的原型链,又不会执行父类构造函数体。如果再被问到es6 classextends本质,可以说它是寄生组合式继承的语法糖,但比 ES5 的实现更严格,比如必须通过 new 调用、类声明不会提升等。

4.3 instanceof 的原理与边界

前面提到过 instanceof,这里再展开一下。它的实现机制可以这样手写:沿着左操作数的原型链逐层查找,看能否找到右操作数的 prototype 对象。这个查找过程就是原型链的遍历。

有一个很值得注意的边界:Object.create(null)创建出来的对象没有原型链,instanceof Object返回 false。另一个容易被追问的边界是Function instanceof FunctionFunction instanceof Object,这两个都是 true,因为 Function 函数的__proto__既可以指向 Function.prototype,又因为 Function.prototype 本身是一个对象,所以也能被 Object 判定。这个“函数也是对象”的层叠关系,能把这句话讲清楚,基本就在这个知识点上建立了优势。

5. 事件循环与异步机制:宏任务微任务的区别,是面试的分水岭

5.1 从调用栈到任务队列,事件循环到底在循环什么

事件循环是 JavaScript 面试中“看起来没难度,问起来全是坑”的领域。核心要理解的是:JavaScript 是单线程的,但浏览器或 Node 环境通过事件循环机制实现了非阻塞的异步处理。单线程意味着只有一个调用栈,一次只能执行一段代码。同步代码按顺序压入调用栈执行,遇到异步操作(如 setTimeout、fetch、Promise 回调)时,不会一直卡住等结果,而是把对应的回调放入任务队列,等当前调用栈清空后再按顺序取出执行。

事件循环的每一次“循环”就是:从宏任务队列里取出一个宏任务执行,执行过程中如果有新的微任务产生,全部添加到微任务队列;当前宏任务执行完毕后,清空整个微任务队列;然后进行必要的渲染;接着进入下一个宏任务。关键点是微任务队列必须“执行到空”,哪怕执行过程中产生了新的微任务,也会在当前宏任务结束前继续执行。

5.2 浏览器事件循环和 Node 事件循环的差异

如果在浏览器问完 setTimeout 和 Promise 的输出顺序后,再追问“Node 里运行结果一样吗”,很多人会卡壳。原因是 Node 的事件循环和浏览器的模型不一样。Node 的事件循环包含多个阶段:timers 阶段执行 setTimeout/setInterval 回调,pending callbacks 阶段执行系统操作回调,idle/prepare 阶段内部使用,poll 阶段检索新的 I/O 事件,check 阶段执行 setImmediate 回调,close callbacks 阶段执行 close 事件回调。每个阶段都有各自的任务队列,每个阶段之间才会清空微任务队列。

这意味着在 Node 里,process.nextTick会在当前阶段结束后立刻执行,优先级高于 Promise 的微任务;setImmediate在 check 阶段执行,setTimeout在 timers 阶段执行,如果在 poll 阶段空闲时调用setImmediate,它会先于下一轮 timers 的 setTimeout 执行。这些细节面试官不一定会问到,但理解了模型的差异,你在任何环境下分析异步代码都不会慌。我一般建议候选人:先记住“微任务永远优先于下一个宏任务”这条铁律,再根据环境差异做细化。

5.3 一道必考的事件循环代码题,我带你完整推演

直接来一道最经典的题,猜输出顺序:

console.log('A'); setTimeout(function() { console.log('B'); Promise.resolve().then(() => console.log('C')); }, 0); Promise.resolve().then(() => { console.log('D'); setTimeout(() => console.log('E'), 0); }); console.log('F');

输出顺序是 A、F、D、B、C、E。完整推演过程:同步代码先执行,打印 A、F;遇到第一个 setTimeout,回调进宏任务队列;遇到 Promise.then,回调进微任务队列;同步代码执行完毕,主调用栈清空,开始处理微任务,打印 D;D 里的 setTimeout 进宏任务队列;微任务队列空了,事件循环取出第一个宏任务(第一个 setTimeout),打印 B;B 的执行过程中注册了 Promise.then,进入微任务队列,B 执行完立刻清空微任务,打印 C;再取下一个宏任务(D 里注册的 setTimeout),打印 E。这里最容易答错的是 C 会不会在 B 后面立刻执行,以及 E 能不能在 B 之前输出。记住一条原则:“当前宏任务清空微任务”优先于“取出下一个宏任务”,就能完全锁定答案。

6. 手写题集锦:防抖节流、深拷贝、数组去重和扁平化

6.1 防抖和节流:说出应用场景比默写代码更重要

手写防抖节流几乎是前端面试的必考题。但面试官真正想考察的,是你有没有在真实场景里用过,以及是否理解两者的区别。简单的记忆方式是:防抖是“等一等再执行”,连续触发时只有最后一次生效;节流是“固定频率执行”,连续触发时每隔一段时间必须执行一次。防抖适合搜索框输入联想,用户停顿了才发起请求;节流适合滚动监听、窗口 resize 计算,避免高频回调。

防抖的标准实现:

function debounce(fn, delay = 300) { let timer = null; return function(...args) { if (timer) clearTimeout(timer); timer = setTimeout(() => { fn.apply(this, args); timer = null; }, delay); }; }

注意fn.apply(this, args)这一行,很多人会漏掉 this 的绑定。如果直接用fn(),内部 this 会丢失上下文。节流用时间戳实现:

function throttle(fn, interval = 300) { let last = 0; return function(...args) { const now = Date.now(); if (now - last >= interval) { last = now; fn.apply(this, args); } }; }

如果你能进一步说出“防抖和节流都有立即执行和延迟执行两种变体,分别用 leading 和 trailing 参数控制”,面试官基本就确定你写过生产级代码了。

6.2 深拷贝:从 JSON 到递归,再到考虑 Map/Set/循环引用

手写深拷贝是基础中的基础,但能写出完全版的候选人很少。第一版大家都会:JSON.parse(JSON.stringify(obj)),然后面试官会追问这个方案的三个致命缺点:无法处理函数、Symbol、undefined;无法处理 RegExp、Date 等特殊对象;循环引用会直接报错。

第二版是用递归手动拷贝。基本的思路是:如果是原始类型直接返回;如果是数组或对象,创建一个新对象,然后递归拷贝每个属性。进阶版要处理 Date、RegExp,可以用new obj.constructor(obj);要判断数组用Array.isArray;循环引用可以用 WeakMap 记录已拷贝的对象,遇到相同的引用直接返回之前拷贝的结果。Symbol 属性可以用Reflect.ownKeys来获取。能写出带 WeakMap 的版本,说明你思考过“为什么这里不能把缓存放到闭包外面”以及“Map 和 WeakMap 的区别”——WeakMap 的键是弱引用,不会影响垃圾回收,专为这类场景设计。

6.3 数组去重和扁平化:核心是考察你对 API 和迭代方式的能力

数组去重至少有五种写法。第一种是Array.from(new Set(arr)),性能最好、代码最简洁。第二种是 filter + indexOf,第三种是 reduce 加 includes,第四种是双重循环,第五种是对象键值法。面试时推荐先说Set,然后提到“如果数组元素是对象,Set 的去重基于引用比较,无法按属性去重,这时候需要自己实现一个按 key 去重的函数”,展示出对边界的思考。

数组扁平化也有几个层次。Array.prototype.flat(Infinity)是最简单的,但面试官通常要求手写。递归版:遍历数组,遇到子数组就递归,把结果 concat 或 push。迭代版:用 reduce 加递归。进阶版:用栈模拟,从后往前遍历压栈,遇到子数组就展开。还有一个很巧妙的写法是用JSON.stringifyreplacesplit,但只适用于纯数字数组,可以作为补充方案提一嘴。栈实现的代码看着简单,但能把这个过程讲清楚的人不多,建议自己推演一遍。

7. 面向面试官的表达:八股文怎么答,才显得不是背的

7.1 黄金三步法:结论先行、原理说明、场景补充

我面试过的候选人里,有基础很好但表达混乱的,也有基础一般但表达清晰让面试官高看一眼的。差异就在表达结构上。我建议大家采用“结论先行、原理说明、场景补充”的三步法。

举个例子,被问到“什么是闭包”。第一句给结论:“闭包是函数以及它定义时所在词法作用域的组合,它让函数能够访问定义时的外部变量,即使外部作用域已经执行结束。”第二句讲原理:“JavaScript 使用词法作用域,函数定义时引擎会保存 [[Environment]] 指向当前词法环境,函数执行时通过作用域链找到外层环境。”第三句补场景:“实际项目中,比如防抖函数的 timer 变量、组合式 API 中的状态管理,都用了闭包来保存私有数据。”这样答下来,面试官不仅听到一个定义,还能看到你的知识是成体系的。

7.2 如何应对“不懂的问题”:先承认,再迁移,再给思路

面试中必然会遇到不会的问题。处理得好,印象分不降反升。我建议的框架是:先坦白说明自己对这块没有深入实践,然后尝试从已有的知识体系中推导关联,最后一句话给出你接下来会怎么去了解它。

比如被问到“V8 引擎的隐藏类(Hidden Class)是什么”,你如果完全没研究过,可以答:“我了解 V8 的优化机制,但对于隐藏类的具体实现细节,我确实没有深入源码层面研究过。不过据我理解,它与 JavaScript 对象动态添加属性会影响内联缓存和性能有关,类似一个对对象形状的快速查找优化。如果项目中遇到性能瓶颈,我会优先去打 profiling,再针对性地查 V8 的编译优化文档。”这样的回答展现出诚实、有知识迁移能力、有解决问题的路径,比硬着头皮编一个答案好得多。面试官要的不是“全知道”,而是“遇到不会的东西,你怎么应对”。

7.3 面试官最容易追加的追问方向,提前准备

每个常见考点背后都有固定的追问链。数据类型后面经常追问 Symbol 和 BigInt 的场景;闭包后面追问内存泄漏和设计模式;this 后面追问 call/apply/bind 的手写;事件循环后面追问 Node 模型和源码执行顺序;Promise 后面追问并行控制和错误捕获方式。

我建议你准备一张自测清单,逐项打勾。let/const/var的区别、暂时性死区的产生原因;async/await和 Promise 的关系,以及 await 后面的代码在哪个队列执行;=====的完整转换规则;MapObject的适用场景;Set和数组的互转方式;WeakMapWeakSet的作用;ProxyObject.defineProperty的区别;模块化规范CommonJSESModule的静态分析差异。这些点如果每个都能用三步法讲两分钟,面试的基础问答环节基本就稳了。

我自己带过的候选人里,每次技术面挂掉,复盘时十有八九都是败在“知道答案”和“讲不清楚”之间的空档。八股文不是不能背,而是背完之后,要花同样的时间把“为什么”和“用在哪儿”补上。你不需要成为 JavaScript 语言的考古学家,但至少要能把每一个常用机制的原理路径讲明白。面试官大多是资深工程师,他们问一个问题时,心里其实有完整的后续追问链,你每答深一层,他给你的评分档位就会往上跳一档。

如果你现在正处于投简历和面试的阶段,我建议你把上面七个主题当成一个持续两天左右的专项复习:第一天过概念和代码题,第二天找人互相模拟追问。手写题一定要亲手写,不要只是在脑子里过一遍——我见过无数候选人“我觉得会了”,到了白板面前就是写不出来。JavaScript 的面试八股,本质上是对基本功的一次系统性检验。把这些内容吃透,你会发现自己不只是面试更稳了,日常 debug 和阅读源码时,很多原来“碰运气”的地方都会变得清晰起来。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/29 21:47:07

claude-skills 12大语言专家横向对比:你的项目该用哪个

claude-skills 12大语言专家横向对比:你的项目该用哪个 【免费下载链接】claude-skills 67 Specialized Skills for Full-Stack Developers. Transform Claude Code into your expert pair programmer. 项目地址: https://gitcode.com/GitHub_Trending/claud/clau…

作者头像 李华
网站建设 2026/8/29 21:39:29

前端面试八股文攻略:从原型链到事件循环的底层逻辑

前端圈这些年一直有个很有意思的现象:一边是社区里天天有人喊"八股文没用,面试造火箭、工作拧螺丝",一边是招聘市场上,简历过了、项目聊得也不错,最后倒在基础题上的候选人一抓一大把。我最近帮团队做了好几…

作者头像 李华
网站建设 2026/8/29 21:39:24

STM32定时器深度解析:从PWM、输入捕获到编码器与高级应用实战

1. 项目概述:深入理解STM32定时器的核心价值如果你正在玩STM32,或者任何一款微控制器,却还没把定时器玩明白,那感觉就像开着一辆跑车却只用一档在市区里转悠——动力和潜力完全被浪费了。定时器,这个看似简单的“计时”…

作者头像 李华
网站建设 2026/8/29 21:33:11

京东2017前端校招主观题复盘:从JS基础到工程化的考点变迁

2017年互联网公司校招季,前端岗位的面试题和现在相比,风格差异其实挺大的。那会儿Vue 2刚发布一年多,React 16还没影儿,TypeScript在中小团队里还算稀罕玩意儿。京东那套主观题,如果你现在翻出来看,会觉得有…

作者头像 李华
网站建设 2026/8/29 21:32:27

windows+ubuntu双系统,可远程切换启动系统

1. Ubuntu操作步骤(UEFI模式): 1.安装工具 sudo apt install efibootmgr 2.查看启动编号 sudo efibootmgr 3.设置下一次启动 sudo efibootmgr -n XXXX 4.立即重启 就会进入设置的下一次启动的系统2.Windows使用第三方命令行工具: bootnext 安…

作者头像 李华