为何React、Next等主流库官方文档普遍使用function declaration而非函数表达式?
首先先澄清两个普遍存在的认知误区:
- 所谓函数表达式解决变量提升问题本质是特性取舍,变量提升并非绝对的缺陷,在很多场景下反而会成为开发优势;
- 二者的性能差异在现代V8、SpiderMonkey等JS引擎的优化下已经可以完全忽略,早年的性能差距早已不存在,并不存在函数表达式绝对性能更优的说法。
函数声明至今仍被大量使用,核心有几个不可替代的优势:
- 提升特性适配代码阅读逻辑
函数声明的提升特性允许开发者把核心业务逻辑放在文件最顶部,工具类辅助函数放在文件底部,阅读代码的人第一眼就能看到最核心的执行流程,不需要先跳过大量辅助实现代码。如果使用函数表达式,必须把所有被调用的函数定义放在调用逻辑之前,核心逻辑会被挤到文件中后段,反而降低可读性。
示例:// 核心逻辑首屏可见 function initApp() { const user = getUserInfo() const menu = genMenu(user.perm) renderPage(menu) } // 辅助函数统一放在底部,无需关注时可以直接跳过 function getUserInfo() { /* 实现逻辑 */ } function genMenu() { /* 实现逻辑 */ } function renderPage() { /* 实现逻辑 */ } - 调试体验更友好
函数声明默认自带确定的函数名,代码运行抛出异常时,堆栈追踪会直接显示对应的函数名,能快速定位问题。而未命名的函数表达式、箭头函数在堆栈中只会显示anonymous,大幅提升调试成本,就算手动给函数表达式命名,大多数开发者也没有这个书写习惯。 - 功能覆盖场景更全
常用的箭头函数表达式属于词法this绑定,无法动态修改this指向,也不能作为构造函数用new调用,无法适配事件回调、类方法定义、构造函数等需要动态this的场景。而函数声明的this为动态绑定,覆盖所有常规函数的使用场景,普适性更强。 - 递归调用更安全
函数声明可以直接通过自身函数名完成递归调用,不存在引用失效的问题。而匿名函数表达式的递归依赖外部变量引用,一旦外部变量被重新赋值,递归就会直接报错,严格模式下也不能用已废弃的arguments.callee实现递归。
示例:// 函数声明递归无风险 function factorial(n) { return n <= 1 ? 1 : n * factorial(n-1) } // 函数表达式递归依赖外部变量,容易出问题 const factorialExpr = n => n <=1 ? 1 : n * factorialExpr(n-1) const tmp = factorialExpr factorialExpr = null tmp(5) // 抛出错误,因为factorialExpr已被赋值为null - 认知成本更低
函数声明的写法和C、Java、Python等绝大多数通用编程语言的函数定义语法对齐,不管是JS入门开发者还是从其他语言转过来的开发者,都能一眼理解其含义,不需要额外区分表达式和声明的语法差异。面向全层级开发者的官方文档选择这种低认知成本的写法,是非常合理的决策。
不同规范的选择都是适配对应场景的结果:Airbnb规范优先规避变量提升可能带来的意外调用风险,统一团队代码风格,因此推荐函数表达式;而官方文档优先考虑普适性、可读性、调试友好,选择函数声明完全符合其面向受众的需求。
内容的提问来源于stack exchange,提问作者Kyungeun Park
相关产品推荐
相关产品推荐

