JavaScript - 函數提升:初學者指南

你好,未來的JavaScript大師!今天,我們將要深入探索JavaScript的一個讓新手上當的迷人特徵:函數提升。不要擔心這聽起來有些神秘——在這堂課結束時,你將會像專家一樣提升函數!

JavaScript - Function Hoisting

什麼是函數提升?

在我們深入細節之前,先來個簡單的定義:

函數提升是JavaScript中的一種行為,在代碼執行之前,函數聲明會被移到它們作用域的頂部。

現在,我知道你們在想什麼:"但是老師,這到底是什麼意思?"讓我們用一些例子來剖析它,好嗎?

示例 1:神奇出現的函數

sayHello(); // 這可以正常工作!

function sayHello() {
console.log("Hello, world!");
}

如果你是編程新手,現在可能會感到困惑。你會問:"我們怎麼能在定義函數之前調用它?"親愛的學生們,這就是函數提升的魔法!

在這個例子中,JavaScript將整個sayHello函數提升到其作用域的頂部。所以,背後的代碼就像這樣:

function sayHello() {
console.log("Hello, world!");
}

sayHello(); // 现在這更有道理了,對吧?

示例 2:兩個函數的故事

讓我們來點刺激的,再看另一個例子:

greeting("John"); // 輸出:"Hello, John!"
farewell("John"); // 錯誤:farewell不是一個函數

function greeting(name) {
console.log("Hello, " + name + "!");
}

var farewell = function(name) {
console.log("Goodbye, " + name + "!");
};

在這兩個函數的故事中,我們看到了不同的行為。由於提升,greeting函數在被調用之前可以正常工作。但是可憐的farewell會拋出錯誤。為什麼?因為只有變量聲明var farewell被提升,而不是函數賦值。

函數提升的規則

現在我們已經看到了函數提升的實際運作,讓我們來制定一些基本規則:

  1. 函數聲明會被完全提升。
  2. 變量聲明會被提升,但不是它們的賦值。
  3. 函數表達式(當你將函數賦值給一個變量)不會被提升。

讓我們用更多的例子來探索這些規則!

示例 3:聲明與表達式

// 這可以工作
hello();

function hello() {
console.log("Hello from a function declaration!");
}

// 這不能工作
goodbye(); // 錯誤:goodbye不是一個函數

var goodbye = function() {
console.log("Goodbye from a function expression!");
};

在這裡,hello是一個函數聲明,所以它被完全提升。但是goodbye是一個函數表達式,所以只有var goodbye部分被提升,函數本身沒有。

JavaScript 變量提升

既然我們已經討論了函數提升,那麼我們快速看一下變量提升。這是一個相關的概念,理解它非常重要。

示例 4:神秘的未定義

console.log(x); // 輸出:undefined
var x = 5;
console.log(x); // 輸出:5

在這個例子中,變量x的聲明被提升,但它的賦值沒有。所以第一次console.log輸出undefined,而第二次顯示賦值的值。

示例 5:Let 和 Const - 新來的 kids

console.log(a); // ReferenceError: 不能在初始化之前訪問'a'
let a = 10;

console.log(b); // ReferenceError: 不能在初始化之前訪問'b'
const b = 20;

隨著ES6中letconst的引入,我們得到了新的行為。這些聲明被提升,但未被初始化。這創造了"暫時死區",在這個區域內你不能訪問變量。

實際含義和最佳實踐

現在我們理解了提升是如何工作的,這對我們開發者來說意味著什麼?

  1. 始終在你們的作用域頂部聲明變量。 這會讓你的代碼更清晰,並防止意外的行為。

  2. 對於你們想在代碼中全程使用的函數,使用函數聲明。 它們的提升行為可以是有益的。

  3. 對於函數表達式要謹慎。 記住,它們不像函數聲明那樣被提升。

  4. 當有疑問時,一起聲明和初始化。 這消除了關於變量值的任何模糊性。

  5. 考慮使用letconst而不是var 它們提供了更可預期的作用域行為。

下面是一個總結不同聲明提升行為的表格:

聲明類型 提升了嗎? 初始化了嗎?
函數聲明
var 未定義
let 否(TDZ)
const 否(TDZ)
函數表達式

結論

這就是它,我正在成長的程序员!我們已經解開了JavaScript中函數提升的謎題。記住,理解這些概念不僅僅是關於知道規則——這是關於寫出更乾淨、更可預測的代碼。

在你們的JavaScript旅程中,你將會遇到更多迷人(有時候也會讓人困惑)的特性。但是不要氣餒!每次你學到新東西,你都是成為JavaScript忍者更近一步。

持續練習,持續編碼,最重要的是,持續提問。畢竟,唯一愚蠢的問題是那個你沒有問的問題!

下次見,快樂編程!

Credits: Image by storyset