面試官:函數聲明和函數表達式有什么區別
在 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,而函數聲明適用于普通函數?!?/span>