JavaScript函数声明、表达式与箭头函数的写法及区别

作者:拾光时间:2026-10-30 18:36:16浏览:607

深入解析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。
JavaScript函数声明、表达式与箭头函数的写法及区别
图1:函数声明与函数表达式在代码预解析与执行阶段的提升机制对比

三、核心区别之二: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);
  }
};
JavaScript函数声明、表达式与箭头函数的写法及区别
图2:普通函数运行期动态绑定与箭头函数词法作用域继承机制对比

四、其他关键技术差异

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)
JavaScript函数声明、表达式与箭头函数的写法及区别
图3:JavaScript三大函数形态核心特性与使用场景汇总矩阵

六、如何选择合适的函数写法:决策指南

在日常项目开发中,选择哪种函数写法不仅关系到语法简洁度,更直接决定了程序的运行时行为。可以按照以下决策路径来判断:

  1. 是否需要作为构造函数实例化(使用 new 操作符)?
    若需要,只能使用传统的函数声明或普通函数表达式(或者ES6的 class 语法糖),严禁使用箭头函数。
  2. 是否属于对象的方法,且方法内部需要通过 this 访问该对象的属性?
    若需要,应使用对象方法简写语法 method() {} 或普通函数表达式,确保 this 正确绑定到调用对象。如果误用箭头函数,this 将指向外部全局或封闭作用域。
  3. 是否属于 DOM 事件监听器,且回调中需要通过 this 引用当前触发事件的元素(currentTarget)?
    若需要,应使用普通函数。若在箭头函数中需要访问该元素,则必须通过事件对象参数 e.currentTarget 获取。
  4. 是否作为高阶函数回调(例如 Array.prototype.map、filter、定时器、Promise链式处理)?
    优先使用箭头函数。箭头函数的精简语法能够显著提升代码可读性,同时天然保留外部上下文的 this,避免传统开发中频繁使用 bind(this)。
  5. 普通业务逻辑与工具函数:
    对于顶层业务模块函数,函数声明具备语义清晰、提升安全的特点;对于仅在模块内部局部流转或按条件赋值的函数,使用 const 声明的函数表达式或箭头函数更为可控。
JavaScript函数声明、表达式与箭头函数的写法及区别
图4:JavaScript函数写法选择与场景匹配决策指南
注意事项与最佳实践: 在现代前端工程(如React、Vue或Node.js项目)中,建议遵循“普通逻辑函数用声明、回调和管道用箭头函数、对象方法用简写、避免使用var”的原则。明确各函数形式在作用域与上下文上的表现,能够从根源上杜绝许多隐蔽的运行时错误。
作者最新文章
相关文章
更多