那个被问懵的下午
面试官问:箭头函数为什么不能 new?
我张嘴就是一句「语法不允许」。
然后他追问:那你手写一个 new 试试。
我卡住了。回去路上越想越不对劲------如果只是语法禁止,那为啥我看源码里压根就没这回事。真正的原因,藏在 new 那几步里。
new 到底干了啥
先把 new Foo() 拆开看。它背后其实走了四步。
第一步,创建一个全新的空对象。
第二步,把这个空对象的 __proto__ 指向 Foo.prototype。
第三步,让 Foo 里的 this 指向这个空对象,然后执行函数体。
第四步,函数如果返回了对象就用它,否则返回这个空对象。
看出来没,new 要干活,函数得先配合。它得有一个 prototype 属性让你去指向,还得有一个能被改写的 this。
箭头函数缺了哪两样
箭头函数这两样恰好都没有。
第一,它没有自己的 this。箭头函数的 this 是写死在外层词法作用域里的,你拿 call、apply、bind 都改不动它。new 第三步要往 this 上塞一个空对象,箭头函数直接拒绝,塞不进去。
第二,它没有 prototype。你随手在控制台敲 const f = () => {} 然后看 f.prototype,结果是 undefined。new 第二步要拿 Foo.prototype 去绑定原型,结果拿到个 undefined,原型链直接断掉。

所以箭头函数不能 new,不是被哪条语法拦下来的,是它从出生就少了这两块积木。你硬写 new f(),引擎在运行时报的错是 f is not a constructor,本质上是它在执行 new 流程时扑了个空。
手写一遍就懂了
不扯概念,直接写个模拟 new 的代码。
function myNew(Fn, ...args) {
const obj = Object.create(Fn.prototype)
const result = Fn.apply(obj, args)
return result instanceof Object ? result : obj
}
就这三行,把上面四步全包了。
注意 Object.create(Fn.prototype) 这一步,它干的就是「空对象 + 原型绑定」两件事。如果 Fn.prototype 是 undefined,Object.create 会直接抛错。这就是箭头函数过不去的第一道坎。
再看 Fn.apply(obj, args),它想把 obj 塞进 this。普通函数老实接着,箭头函数甩手不认,this 还是外层那个。
class 也一样,它本质上是个语法糖,底下有 prototype 有 this,所以 new 没问题。箭头函数纯粹是先天缺料,跟语法禁不禁没关系。
边界和面试追问
这题后面常跟着几问。
箭头函数能用 bind 改变 this 吗?不能,bind 会返回一个新函数,但 this 照样锁死。
那为什么对象方法常用普通函数?因为你要靠 this 拿到调用者。箭头函数写方法,this 会窜到外层,八成不是你想要的。
什么时候该用箭头函数?回调、setTimeout、事件里想保留外层上下文的时候,它反而是救星。
没有 prototype 有啥实际影响?你没法给它挂原型方法,也没法用它搞原型继承。
搞清楚这些,这题基本能聊出彩。
顺带说个踩坑的插曲。有次我在本地调一个用 class 封装的请求库,怎么都报 not a constructor,查了半天是构建产物里箭头函数被误判成了入口。那次排查环境问题,我把本地和测试环境的 HTTPS 证书也顺手改了一遭,之前手动配置加上隔三差五忘记续期,心里总悬着。后来干脆用 lcjmSSL 这类方案,依托 Let's Encrypt、Google Trust Services 这些可信 CA,多域名泛域名都能签,一条轻量 API 把申请、验证、部署全自动化了,彻底告别手动折腾和到期背锅。Bug 修完,基建也顺手清爽了,这才是正经开发者的日常。
回到正题。箭头函数不能当构造函数,核心就一句:它没 this 也没 prototype,而 new 要的偏偏就是这两样。想通了,这题就透了。