在 JavaScript 中,我们可以通过 函数声明(Function Declaration) 和 函数表达式(Function Expression) 来定义函数。它们在提升(Hoisting)、可读性、作用域等方面有所不同。
1. 语法区别
函数声明(Function Declaration)
function sayHello() {
console.log("Hello, world!");
}
特点:
- 使用 function 关键字直接定义函数。
- 必须有函数名(sayHello)。
- 可被提升(Hoisting),可以在声明前调用。
函数表达式(Function Expression)
const sayHello = function() {
console.log("Hello, world!");
};
特点:
- 将函数赋值给变量(const sayHello = ...)。
- 匿名函数(Anonymous Function)或具名函数(Named Function Expression):
a.匿名函数:function() {}(大多数情况下使用)
b.具名函数:function myFunc() {}(仅在调试时有用)
- 不会被提升(Hoisting),只能在定义后调用。
2. 提升(Hoisting)
函数声明会被提升
函数声明在 JavaScript 解析阶段 就会被提升,因此可以在声明前调用:
sayHello(); // ✅ 可以调用
function sayHello() {
console.log("Hello, world!");
}
解析阶段:
// 解析时,相当于:
function sayHello() {
console.log("Hello, world!");
}
sayHello(); // 正常执行
函数表达式不会被提升
函数表达式不会被提升,只有在执行到赋值代码后才可以调用:
sayHello(); // ❌ 报错:Cannot access 'sayHello' before initialization
const sayHello = function() {
console.log("Hello, world!");
};
解析阶段:
const sayHello; // 变量存在,但未赋值
sayHello(); // ❌ 报错
sayHello = function() { console.log("Hello, world!"); };
结论:
- 函数声明:可在定义前调用
- 函数表达式:必须先定义再调用
3. 作用域 & 调试
具名函数表达式的优势
如果使用 具名函数表达式(Named Function Expression, NFE):
const sayHello = function greet() {
console.log("Hello, world!");
};
sayHello(); // ✅ 正常调用
greet(); // ❌ 报错:greet is not defined
- sayHello() 可用
- greet() 仅在函数内部可用(局部作用域),但在外部不可访问。
- 好处:
a.提升调试能力:错误栈中会显示 greet,而不是 anonymous function。
4. 是否可用作 IIFE(立即执行函数表达式)
函数表达式支持 IIFE
(function() {
console.log("立即执行!");
})(); // ✅ 立即执行
- IIFE(Immediately Invoked Function Expression,立即执行函数) 需要函数表达式。
- 函数声明无法用于 IIFE,否则会报错:
function() {
console.log("错误示例");
}(); // ❌ 语法错误
结论:
- IIFE 只能用函数表达式,不支持函数声明.
5. 面试高频问题
面试官:函数声明和函数表达式的本质区别?
回答:
- 函数声明会被提升,可以在定义前调用;函数表达式不会被提升,只能在赋值后调用。
- 函数声明更适合普通函数,函数表达式更适合动态函数、回调和 IIFE。
面试官:函数表达式能被提升吗?回答:
- 变量名会被提升,但不会赋值。所以不能在定义前调用:
console.log(myFunc); // ❌ undefined
const myFunc = function() {}; // 这里才赋值
面试官:什么时候用函数表达式?回答:
- 当函数是回调时(如 setTimeout、事件监听器)。
- 当函数需要立即执行(IIFE)。
- 当需要匿名或具名函数表达式(例如递归或调试)。
总结
对比项 | 函数声明 | 函数表达式 |
定义方式 |
|
|
提升(Hoisting) | ✅ 提升,可在声明前调用 | ❌ 不提升,必须先定义再调用 |
是否必须命名 | ✅ 必须有函数名 | ✅ 匿名或具名 |
适合场景 | 适用于普通函数 | 适用于回调、IIFE |
是否支持 IIFE | ❌ 不支持 | ✅ 支持 |
面试高分回答:
“函数声明会被提升,可以在定义前调用,而函数表达式不会提升,必须先赋值再调用。函数表达式适用于回调、IIFE,而函数声明适用于普通函数。”