現象對比
// 情況 1:函數聲明 + 函數表達式
function m1() { console.log('m1 one'); }
m1(); // m1 one
var m1 = function() { console.log('m1 two'); };
m1(); // m1 two
// 情況 2:兩個函數聲明
function m1() { console.log('m1 one'); }
m1(); // m1 two (已被下面覆蓋)
function m1() { console.log('m1 two'); }
m1(); // m1 two
解析階段機制
- 掃描階段:JS 引擎先掃描所有
function 聲明和 var 變量聲明,建立作用域綁定。- 函數聲明:完整定義(名+體)提升到作用域頂部。
- 變量聲明:僅聲明提升(
var m1; // undefined),賦值留在原處。
- 執行階段:按代碼順序執行賦值與調用。
情況 1 等價代碼
var m1; // 掃描 var 提升
function m1() { console.log('m1 one'); } // 掃描 function 提升
m1(); // 輸出 one
m1 = function() { console.log('m1 two'); }; // 執行賦值,覆蓋 m1
m1(); // 輸出 two
情況 2 等價代碼
function m1() { console.log('m1 one'); } // 第一個聲明
function m1() { console.log('m1 two'); } // 第二個聲明覆蓋第一個
m1(); // two
m1(); // two
最佳實踐
- 避免同名函數聲明與變量聲明混用。
- 優先使用
const/let 定義函數表達式(const m1 = () => {}),無提升、暫時性死區防止誤用。