JavaScript中两种函数声明方式的比较

JavaScript中两种函数声明方式的比较

技术背景

在JavaScript开发中,函数是一等公民,有着至关重要的地位。在维护他人的JavaScript代码时,常常会遇到两种不同的函数声明方式:

1
2
3
4
5
6
7
var functionOne = function() {
// Some code
};

function functionTwo() {
// Some code
}

这两种声明方式在语法上有所不同,其背后的原理和使用场景也存在差异。理解它们的区别,有助于我们编写出更清晰、更高效的代码。

实现步骤

函数声明(Function Declaration)

函数声明是一种独立的声明语句,不需要紧跟分号(添加分号也无害)。在执行进入包含该声明的上下文时,函数声明会被提前处理,在任何逐行代码执行之前,函数就已经被创建并放入相应的作用域中。

1
2
3
4
5
6
// 可以在声明之前调用函数
functionTwo();

function functionTwo() {
console.log("Hello!");
}

在上述代码中,尽管functionTwo()的调用在函数声明之前,但由于函数声明的提升特性,代码可以正常运行。

函数表达式(Function Expression)

函数表达式是一种表达式,通常作为变量赋值的一部分。它在代码逐行执行到该表达式时才会被计算和赋值。

1
2
3
4
5
6
// 会抛出TypeError,因为此时functionOne还未被赋值为函数
functionOne();

var functionOne = function() {
console.log("Hello!");
};

在这个例子中,由于函数表达式是在代码执行到赋值语句时才会生效,所以在赋值之前调用functionOne会导致错误。

核心代码

函数声明的提升示例

1
2
3
4
5
6
// 可以正常调用,因为函数声明被提升到顶部
foo();

function foo() {
console.log('Function Declaration');
}

函数表达式的赋值示例

1
2
3
4
5
6
// 抛出TypeError,因为bar还未被赋值为函数
bar();

var bar = function() {
console.log('Function Expression');
};

结合两种方式的示例

1
2
3
4
5
6
7
8
9
10
11
12
var xyz = function abc(){};

// xyz可以正常使用
xyz();

// abc在外部作用域中未定义(除了IE)
console.log(typeof abc); // "undefined"

// 在函数内部可以使用abc
var xyz = function abc(){
console.log(typeof abc); // "function"
};

最佳实践

函数声明的使用场景

  • 全局或模块级别的函数:当需要在整个文件或模块中都可以访问的函数时,使用函数声明。因为函数声明的提升特性,函数可以在声明之前被调用,方便代码的组织和调用。
  • 递归函数:函数声明可以确保函数在其定义之前就已经存在于作用域中,适合用于递归调用。
1
2
3
4
5
6
7
8
function factorial(n) {
if (n === 0) {
return 1;
}
return n * factorial(n - 1);
}

console.log(factorial(5)); // 120

函数表达式的使用场景

  • 匿名函数:当函数只在某个特定的地方使用,不需要全局访问时,可以使用函数表达式创建匿名函数。例如,作为回调函数传递给其他函数。
1
2
3
setTimeout(function() {
console.log('This is a callback function');
}, 1000);
  • 控制函数的可见性:函数表达式可以更好地控制函数的作用域,避免全局污染。通过将函数赋值给局部变量,可以确保函数只在当前作用域内可见。
1
2
3
4
5
6
7
8
9
function outer() {
var inner = function() {
console.log('Inner function');
};
inner();
}

outer();
// 无法在outer函数外部访问inner函数

常见问题

函数声明在块级作用域中的表现

在ES2015之前,规范没有明确规定函数声明在控制结构(如iftry等)中的行为,不同浏览器的实现存在差异。在ES2015及以后,严格模式下函数声明具有块级作用域。

1
2
3
4
5
6
7
8
9
10
"use strict";
if (true) {
// 可以在块内调用
foo();
function foo() {
console.log('Block-scoped function');
}
}
// 在块外无法访问
console.log(typeof foo); // "undefined"

函数表达式中函数名的问题

在ES5中,使用函数表达式创建的函数通常是匿名的,但在ES2015中,函数会尝试从上下文推断名称。

1
2
3
4
var y = function () {
console.log('y');
};
console.log(y.name); // 在ES2015及以后,输出 "y"

性能差异

不同的JavaScript引擎对函数声明和函数表达式的性能处理略有不同,但在大多数情况下,性能差异可以忽略不计。不过,在某些特定场景下,如在循环中大量创建函数,可能需要进行性能测试来选择更合适的方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 测试函数声明的性能
console.time('Function Declaration');
for (let i = 0; i < 1000000; i++) {
function test() {}
}
console.timeEnd('Function Declaration');

// 测试函数表达式的性能
console.time('Function Expression');
for (let i = 0; i < 1000000; i++) {
var test = function() {};
}
console.timeEnd('Function Expression');

JavaScript中两种函数声明方式的比较
https://119291.xyz/posts/2025-04-16.comparison-of-two-function-declaration-methods-in-javascript/
作者
ww
发布于
2025年4月16日
许可协议