JavaScript 函数声明与函数表达式的核心区别及使用规范

一只小鹿
一只小鹿
发布时间:2026-10-14 | 6人浏览过
原创

理解 JavaScript 中的函数定义机制是掌握该语言的关键。本文将对比函数声明与函数表达式的解析差异、作用域行为及适用场景,帮助开发者规避条件语句中定义函数的陷阱,编写更规范的代码。

两种创建函数对象的方法

在 ECMAScript 中,最常用的两种创建函数对象的方法是使用 函数声明 (Function Declaration) 或 函数表达式 (Function Expression)。区分这两者的一个硬性标准是:函数声明必须始终带有一个标识符(即函数名),而函数表达式则可以省略标识符,也可以是具名的(但具名函数表达式的内部名称仅在该函数作用域内可见)。

第1步:识别函数声明

function Identifier ( FormalParameterList opt){ FunctionBody }

函数声明的解析规则如下:

  1. 创建一个 new Function 对象,参数列表和函数体由定义指定。当前运行环境的作用域链将成为它的作用域。
  2. 在当前变量对象中创建一个名为 Identifier 的属性,其值为新创建的函数对象。

第2步:识别函数表达式

函数表达式可以是匿名的或具名的:

复制代码 代码如下:
 function Identifier opt( FormalParameterList opt){ FunctionBody }  // 这里是具名函数表达式

具名函数表达式的解析步骤较为复杂:

  1. 创建一个 new Object 对象。
  2. 将该对象添加到作用域链的顶端。
  3. 创建 new Function 对象,使用当前执行环境的作用域链。
  4. 为新创建的对象(Result 1)创建一个名为 Identifier 的属性,指向该函数对象。此属性只读且不可删除。
  5. 从作用域链中移除 Result 1。
  6. 返回创建的函数对象。

简单来说,ECMAScript 是通过上下文来区分两者的:如果 function foo(){} 是一个赋值表达式的一部分,则被视为函数表达式;如果它被包含在函数体内或位于程序的最上层,则作为函数声明解析。

核心区别:作用域提升与求值时机

理解两者行为的关键在于:函数声明会在任何表达式被解析和求值之前先行被解析和求值。即使声明位于源代码的最后一行,它也会先于同一作用域中位于最前面的表达式被求值。

第3步:验证声明的提升特性

下面这个例子展示了这种“提升”现象。尽管函数 fn 是在 alert 调用之后才定义的,但在 alert 执行时,fn 已经存在于作用域中:

复制代码 代码如下:
alert(fn()); // 输出 'Helloworld!'

function fn() {
  return 'Helloworld!';
}

相对地,表达式在遇到时才进行运算。这意味着如果在调用前未赋值,将会报错。例如,以下代码会抛出 ReferenceError:

复制代码 代码如下:
// 错误!函数表达式没有提升
console.log(bar);
var bar = function() {};

特殊情况:括号分组与上下文解析

有时候函数的位置看似相同,但由于操作符的影响,解析结果完全不同。

第4步:分析分组操作符的影响

将函数包裹在圆括号 () 中构成了分组操作符,而分组操作符只能包含表达式。因此,下面的写法虽然看起来像声明,但实际上是函数表达式:

复制代码 代码如下:
(function foo(){}); // 函数表达式:注意它被包含在分组操作符中

同样,如果是赋值操作或作为 `New` 表达式的一部分,也属于表达式:

复制代码 代码如下:
function foo(){}; // 声明
var bar = function foo(){}; // 表达式
new function bar(){}; // 表达式

高风险操作:条件语句中的函数定义

这是一个非常容易被忽视的坑。根据 ECMAScript 严格规范,函数声明只能出现在 Program(程序)或 FunctionBody(函数体)中,而不能出现在 Block(块)(如 if、while 或 for 语句内的代码块)。然而,在浏览器实现早期,为了向后兼容,某些引擎允许在块内出现函数声明,导致跨平台/跨版本的行为不一致。

第5步:避免非标准化的块级函数声明

切勿在块中直接使用条件函数声明,因为这会导致不可预测的结果:

复制代码 代码如下:
// 千万不要这样做!
if (true) {
  function foo() { return 'first'; }
}
else {
  function foo() { return 'second'; }
} // foo(); // 在不同环境中可能得到 undefined 或错误的函数引用

正确做法是使用函数表达式,将变量提升的特性结合赋值逻辑来控制行为:

复制代码 代码如下:
var foo;
if (true) {
  foo = function() {
    return 'first';
  };
}
else {
  foo = function() {
    return 'second';
  };
}

何时使用哪种定义方式?

总结来看,遵循以下规则可以避免绝大多数陷阱:

  • 使用函数声明的场景:当你的函数需要独立存在,不依赖于变量赋值,且位于全局作用域或嵌套函数作用域顶层时。这是最常见的方式,因为具有提升特性,调用顺序更灵活。
复制代码 代码如下:
// 全局环境
function foo() {} function local() {   // 局部环境
  function bar() {}   return bar; }
  • 使用函数表达式的场景:当你需要将函数作为参数传递、在对象字面量中定义方法、或者需要通过条件判断动态赋值时。
复制代码 代码如下:
// 匿名函数表达式(常用于回调)
callMe(function () { });
// 具名函数表达式
callMe(function me() { });
// 对象字面量中的方法
var myobject = {   say: function () { } };

以上就是对 JavaScript 函数声明与函数表达式区别的详细内容,更多关于 JavaScript 基础概念的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表