函数声明与箭头函数并非仅仅是语法层面的“换种写法”,它们在底层机制上存在本质差异。函数声明会创建具名绑定并参与完全提升,如同自带光环的主角;而箭头函数属于表达式求值,遵循变量声明规则,相对低调。理解这些核心区别,有助于在 JavaScript 开发中规避常见陷阱,提升代码质量。

语法结构与关键字
函数声明必须使用 function 关键字开头,后面紧跟函数名和括号参数列表,例如:
function greet(name) { return 'Hello, ' + name; }- 函数名是强制要求,构成独立的标识符绑定
- 不能省略
function,也不能用const或let包裹——因为它本身就是声明语句
箭头函数则恰好相反,去掉了 function,改用 => 连接参数和函数体:
const greet = (name) => 'Hello, ' + name;- 只有一个参数时可以省略括号:
id => users.find(u => u.id === id) - 没有参数或者有多个参数时,括号必须保留:
() => console.log('hi')或(a, b) => a + b - 返回对象字面量时需要在外层套上括号:
() => ({ x: 1 })——否则引擎会将花括号视为函数体块
作用域与提升行为
函数声明会经历**完全提升**:函数名和函数体在作用域顶部就已经准备就绪,如同提前埋伏好的伏兵:
foo(); function foo() { console.log(1); }—— 正常运行,毫无报错- 可以在声明之前任意调用,非常适合用来组织模块顶层的工具函数
箭头函数本质上是赋值表达式,因此**不提升**:
bar(); const bar = () => {};—— 直接抛出ReferenceError- 行为与
const bar = ...完全一致,受const/let声明规则的约束,在赋值之前访问会得到未定义的引用错误
this 和执行上下文
函数声明拥有独立的执行上下文:
this的指向由调用方式决定。例如obj.fn()中this指向obj,而直接调用fn()则指向全局对象(严格模式下为undefined)- 支持
arguments对象、new调用、prototype属性——这些特性是箭头函数所不具备的 - 可以通过
call/apply/bind动态修改this,灵活性极强
箭头函数则没有自己的执行上下文,而是直接继承外层词法作用域:
this是固定的,无论使用何种方式调用或尝试bind,它都指向定义时的外层this- 没有
arguments对象,获取参数列表只能借助剩余参数...args - 不能用作构造函数被
new调用,也没有prototype属性——简而言之,它属于轻量化函数
命名与调试支持
函数声明天生具备名字,这对开发体验非常友好:
func.name返回真实函数名,在堆栈、断点、性能分析中一目了然- 支持递归调用,比如
function factorial(n) { return n )—— 注意此递归写法存在瑕疵,但语法上允许
箭头函数默认是匿名的:
const fn = () => {};之后,fn.name是空字符串或者'anonymous',具体取决于引擎实现- 调试器中显示为
anonymous,出错时堆栈信息全部是anonymous,定位问题难度明显增加 - 如果想赋予它一个名称,只能借助变量名间接体现,但堆栈不会自动识别该变量名,排查时仍需猜测
