JavaScript函数声明、表达式与箭头函数的写法及区别
深入解析JavaScript中函数声明、函数表达式与箭头函数的基本写法、执行机制与核心区别,涵盖变量提升、this词法绑定、arguments对象及构造调用等关键场景。
在JavaScript开发中,定义函数主要有三种方式:函数声明(Function Declaration)、函数表达式(Function Expression)以及ES6引入的箭头函数(Arrow Function)。虽然它们最终都能创建可执行的函数逻辑,但在变量提升机制、作用域规则、this绑定、参数对象以及是否具备构造函数能力等方面存在本质区别。理解并掌握这三种写法的运作细节,是编写健壮、可维护代码的前提。
一、三种函数的标准写法
1. 函数声明(Function Declaration)
函数声明使用关键字 function 开头,后跟强制要求的函数名称、形参列表以及包含在大括号内的函数体。它作为一个独立的语句存在于当前作用域中:
// 函数声明:必须包含函数名
function calculateArea(width, height) {
return width * height;
}
const area = calculateArea(10, 5); // 50
2. 函数表达式(Function Expression)
函数表达式是将一个函数作为值赋给变量或常量。函数表达式可以是匿名的,也可以是具名的(Named Function Expression)。具名表达式中的函数名仅在函数体内部可用,常用于递归调用或调试堆栈分析:
// 匿名函数表达式
const calculatePerimeter = function(width, height) {
return 2 * (width + height);
};
// 具名函数表达式(factorial 仅在函数内部可访问)
const getFactorial = function factorial(n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
};
3. 箭头函数(Arrow Function)
箭头函数是ES6标准引入的语法,使用 =>(胖箭头)连接参数和函数体。当函数只有一个参数时可以省略括号;当函数体只有一条表达式返回语句时,可以省略大括号并隐式返回值:
// 多参数完整形式
const add = (a, b) => {
return a + b;
};
// 单参数简写与隐式返回
const double = n => n * 2;
// 隐式返回对象字面量(必须用小括号包裹对象,避免与代码块混淆)
const createPoint = (x, y) => ({ x, y });
二、核心区别之一:变量提升(Hoisting)机制
JavaScript引擎在执行代码前会经历编译解析阶段与实际执行阶段。函数声明与函数表达式在提升行为上存在显著差异:
- 函数声明: 函数声明会被整体提升到当前作用域的顶部,包括函数名与完整的函数实现。这意味着读者可以在声明该函数之前就安全地调用它。
- 函数表达式与箭头函数: 其提升行为完全取决于承载它的变量关键字(
var、let或const)。如果使用var声明,仅变量名被提升且初始值为undefined,在赋值之前调用会抛出TypeError: ... is not a function;如果使用let或const声明,变量存在暂存死区(TDZ),在赋值行之前访问直接抛出ReferenceError。
三、核心区别之二:this 绑定机制
this 的指向是JavaScript中极易混淆的概念,函数声明、函数表达式与箭头函数在 this 绑定处理上完全不同:
1. 普通函数的动态 this(函数声明与表达式)
普通函数的 this 是在运行时动态决定的,取决于该函数如何被调用:
- 作为对象方法调用时(如
obj.fn()),this指向该对象obj; - 作为独立函数调用时(如
fn()),非严格模式下this指向全局对象(浏览器中为window),严格模式下为undefined; - 使用
call、apply或bind可以显式重定向this。
2. 箭头函数的词法 this(Lexical this)
箭头函数没有自己的 this。它在定义时捕获其外层(定义处所处的作用域)的 this 值作为自己的 this,且在生命周期内永久固定:
- 箭头函数的
this无法通过call()、apply()或bind()更改; - 非常适合用作异步回调(例如
setTimeout、Promise链式调用),无需手动编写const that = this或.bind(this); - 不适合作为对象字面量的方法,否则
this将穿透对象指向外层上下文。
const user = {
name: "Alice",
// 普通函数:this 动态绑定到调用它的对象 user
showNormal() {
setTimeout(function() {
// 这里的普通函数被独立调用,this 指向 window (非严格模式) 或 undefined
console.log("Normal:", this.name); // 输出: undefined
}, 100);
},
// 箭头函数:继承外层 showArrow 的词法 this(即 user 对象)
showArrow() {
setTimeout(() => {
console.log("Arrow:", this.name); // 输出: "Alice"
}, 100);
}
};
四、其他关键技术差异
1. arguments 参数对象
函数声明与函数表达式在函数体内默认存在类数组对象 arguments,包含调用时传入的所有实参:
function sumAll() {
// arguments 是类数组,具有 length 和索引
let total = 0;
for (let i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}
箭头函数没有 arguments 对象。如果在箭头函数内部使用 arguments,它会沿作用域链访问外层普通函数的 arguments。在箭头函数中获取不定参数,必须使用ES6的剩余参数(Rest Parameters)语法:
const sumAllArrow = (...numbers) => {
// numbers 是纯正的 Array 实例
return numbers.reduce((acc, curr) => acc + curr, 0);
};
2. 作为构造函数(new 调用)与原型原型链
- 函数声明与普通函数表达式: 拥有
prototype属性,可以作为构造函数配合new关键字实例化对象。 - 箭头函数: 内部缺少
[[Construct]]方法,没有prototype属性。对其使用new关键字会立即抛出TypeError: ... is not a constructor。
3. 生成器函数(Generator)支持
函数声明与普通表达式可以通过 function* 声明为生成器(Generator),利用 yield 暂停与恢复执行;而箭头函数无法使用 yield 关键字,不能被声明为生成器。
五、全维度对比总结
将上述各个特性的具体表现整合如下:
| 特性维度 | 函数声明 | 函数表达式 | 箭头函数 |
|---|---|---|---|
| 语法结构 | 独立语句,必须有函数名 | 赋值表达式,支持匿名/具名 | (args) => expr 极简语法 |
| 提升行为 (Hoisting) | 完整提升(名称与函数体) | 取决于声明变量 (TDZ / undefined) | 取决于声明变量 (TDZ / undefined) |
| this 绑定机制 | 运行时动态决定(调用者) | 运行时动态决定(调用者) | 词法继承(捕获定义处外层 this) |
| call/apply/bind 效果 | 可自由改变 this 指向 | 可自由改变 this 指向 | 无法更改 this 指向 |
| arguments 对象 | 原生支持类数组 arguments | 原生支持类数组 arguments | 无自身 arguments(需用 ...rest) |
| new 构造调用 | 支持,具有 prototype 属性 | 支持,具有 prototype 属性 | 不支持(抛出 TypeError) |
六、如何选择合适的函数写法:决策指南
在日常项目开发中,选择哪种函数写法不仅关系到语法简洁度,更直接决定了程序的运行时行为。可以按照以下决策路径来判断:
- 是否需要作为构造函数实例化(使用
new操作符)?
若需要,只能使用传统的函数声明或普通函数表达式(或者ES6的class语法糖),严禁使用箭头函数。 - 是否属于对象的方法,且方法内部需要通过
this访问该对象的属性?
若需要,应使用对象方法简写语法method() {}或普通函数表达式,确保this正确绑定到调用对象。如果误用箭头函数,this将指向外部全局或封闭作用域。 - 是否属于 DOM 事件监听器,且回调中需要通过
this引用当前触发事件的元素(currentTarget)?
若需要,应使用普通函数。若在箭头函数中需要访问该元素,则必须通过事件对象参数e.currentTarget获取。 - 是否作为高阶函数回调(例如
Array.prototype.map、filter、定时器、Promise链式处理)?
优先使用箭头函数。箭头函数的精简语法能够显著提升代码可读性,同时天然保留外部上下文的this,避免传统开发中频繁使用bind(this)。 - 普通业务逻辑与工具函数:
对于顶层业务模块函数,函数声明具备语义清晰、提升安全的特点;对于仅在模块内部局部流转或按条件赋值的函数,使用const声明的函数表达式或箭头函数更为可控。
JS处理含Emoji或中文的字符串转数组乱码问题
2026-11-05 16:12:28
JS怎么使用展开运算符合并对象
2026-11-05 14:42:18
ES6+环境下判断JS对象为空的简洁写法
2026-11-05 12:12:23
怎么通过replacer函数自定义JS中JSON的格式化输出
2026-11-05 11:19:44
JS正则表达式基础:字面量与RegExp构造函数的区别及常用元字符
2026-11-05 11:16:58








