技术探索与实践踩坑记录:一个被裁员后靠死磕 JavaScript 面试题翻身的上海码农自白
去年十月,我坐在张江某大厂工位上,HR敲了敲我桌沿:“小陈,方便聊聊吗?”
那天下午三点十七分,阳光斜照进玻璃幕墙,我的 MacBook 屏幕上还开着一个没跑通的 React 组件。十分钟后,我抱着纸箱站在楼下,手里攥着 N+1 的赔偿单——月薪 22k,拿了 3 个月,税后不到 6 万。而我和女朋友合租在浦东三林,月租 3500,房租、水电、吃饭、交通……算下来每月刚性支出至少 8000。
那一刻,我脑子里不是“天塌了”,而是:“完了,下个月面试要是再挂,连泡面都得省着吃。”
被现实毒打:面试题挑战里的“综合”陷阱
裁员后的第一周,我疯狂投简历。结果?简历石沉大海。第二周,终于有几个猎头找上门,但一问技术栈,对方语气立马变冷:“你这 JS 基础……好像不太扎实啊?”
我懵了。我在上一家公司干了三年前端,天天写 Vue + TypeScript,搞微前端、性能优化、CI/CD 流水线,怎么就“基础不扎实”了?
直到我去面了一家 B 轮 startup,面试官甩给我一道题:
“请手写一个
Promise.allSettled的 polyfill,并解释它和Promise.all的区别。另外,如果传入的数组里有非 Promise 元素,你怎么处理?”
我当场卡壳。脑子里只有“用 Promise.all 不就行了吗?”这种 naive 想法。结果面试官微微一笑:“我们这边对 JS 底层理解要求比较高,建议你再夯实一下基础。”
回家路上,地铁 2 号线挤得像沙丁鱼罐头,我盯着手机刷 LeetCode 和 GitHub 上的高频面试题,突然意识到一个问题:我一直在“用” JavaScript,却从来没真正“懂”它。
死磕 JS:从“会用”到“懂原理”的痛苦转型
接下来一个月,我给自己定了个计划:每天下班(哦不对,现在是“待业”)后,雷打不动 3 小时,专攻 JavaScript 核心机制。
我翻烂了《JavaScript 高级程序设计》第四版,重读了 You-Dont-Know-JS 系列,甚至去 MDN 一行行啃规范。重点攻克几个方向:
- 事件循环(Event Loop):宏任务 vs 微任务,Node 和浏览器的差异
- 闭包与作用域链:不只是“能访问外层变量”,而是 V8 如何实现的
- 原型链与 this 绑定:call/apply/bind 的底层模拟
- 异步控制流:Generator + co、async/await 编译后的样子
- 内存管理:V8 的垃圾回收机制,如何避免内存泄漏
最让我崩溃的是手写题。比如:
// 实现一个带并发限制的请求调度器
function requestScheduler(urls, max, callback) {
// ...
}
我一开始写的版本,要么并发控制失效,要么回调顺序错乱。后来才发现,关键在于用 Promise.race + 递归控制队列,还得处理 reject 情况。光是调试就花了两天。
那段时间,女友看我天天对着电脑皱眉,有次晚饭时说:“要不咱先找个工作过渡下?送外卖也行。”
我苦笑:“送外卖月薪可能还没我之前税后高,而且……我不想放弃技术路线。”
她没说话,默默给我盛了碗汤。
踩坑实录:那些看似简单却暗藏杀机的“综合”题
真正让我开窍的,是一道“综合”题——它把闭包、this、原型、异步全揉在一起:
function Foo() {
getName = function () { console.log(1); };
return this;
}
Foo.getName = function () { console.log(2); };
Foo.prototype.getName = function () { console.log(3); };
var getName = function () { console.log(4); };
function getName() { console.log(5); }
Foo.getName(); // ?
getName(); // ?
Foo().getName(); // ?
getName(); // ?
new Foo.getName(); // ?
new Foo().getName(); // ?
new new Foo().getName(); // ?
第一次做,我错了 4 道。原因?忽略了变量提升、函数声明覆盖、this 绑定优先级、new 运算符的执行顺序。
这题背后其实是对 JS 执行上下文、作用域、对象模型的“综合”考察。很多同学只背答案,但面试官真正想看的是你能不能拆解问题。
我后来总结了一个方法论:遇到复杂 JS 题,先问自己三个问题:
- 这段代码在哪个阶段执行?(编译期 vs 运行期)
- 当前 this 指向谁?(全局、对象、undefined?)
- 变量从哪查找?(作用域链 or 原型链?)
这套思路帮我过了后面 5 场技术面。
转机:用“实践”反哺“理论”
光刷题还不够。我决定做个 side project:用原生 JS 写一个 mini React —— 支持 JSX 编译、虚拟 DOM diff、组件生命周期。
名字我都想好了,叫 TinyReact。
过程中踩了无数坑:
- JSX 转换用 Babel 插件还是自己写 parser?(最后选了 Acorn + 自定义 visitor)
- 如何高效 diff?(借鉴了双端比较,但没处理 key 复用)
- setState 是同步还是异步?(得模拟事务批处理)
但正是这些“造轮子”的过程,让我真正理解了 React 底层为什么这么设计。再去面字节的时候,面试官问:“React 18 的自动批处理是怎么实现的?”
我直接画了个微任务队列图,解释 Fiber 如何 hijack 原生 API,他眼睛一亮:“你这理解很到位啊。”
两周后,offer 到手,月薪 32k,涨幅近 50%。
思考:技术人的护城河到底是什么?
回过头看,我最大的误区,是把“熟练使用框架”等同于“掌握技术”。
但实际上,框架会过时,API 会变,但语言核心机制永远不过时。
JavaScript 尤其如此。它看似简单,var a = 1 谁都会写,但一旦深入,你会发现:
- 它的灵活性是一把双刃剑
- 它的“怪异”行为背后都有规范依据
- 它的生态繁荣恰恰源于底层的开放性
所以,别再抱怨“面试造火箭,上班拧螺丝”。如果你只会拧螺丝,那当然觉得造火箭离谱。但如果你理解火箭原理,哪怕拧螺丝,也能拧出花来。
写给正在焦虑的你
我现在依然住在浦东那个老小区,房租涨到了 3800。但心态不一样了。
上周五晚上,和女友在厨房煮火锅,她突然说:“你现在讲话都不一样了,以前总说‘这个需求好傻’,现在会说‘这个设计其实有 trade-off’。”
我笑了。是啊,技术探索的过程,也是心智成熟的过程。
如果你也在被裁员、被拒、自我怀疑,我想说:
别怕踩坑,坑里才有真知。
别嫌题难,难题才筛真人。
JavaScript 不是玩具,它是武器。
而你要做的,就是磨锋它,握紧它,然后——
在下一场战役里,砍出自己的路。
共勉。

评论 0