跳至主要内容

2 篇文章 含有標籤「JS」

檢視所有標籤

JavaScript Hoisting 入門教學筆記 | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

1. 什麼是 Hoisting?

Hoisting(提升)是 JavaScript 中的一種行為,它允許變數與函式在執行時被提升到作用域的頂部,這表示你可以在宣告之前使用它們,而不會發生錯誤。「建立期」(Creation Phase)和「執行期」(Execution Phase),建立期主要為定義變數名稱,執行期為執行程式和指定賦值。

在 JavaScript 中,Hoisting 影響兩種類型的宣告:

  1. 變數宣告(var, let, const)
  2. 函式宣告(Function Declaration)

2. 變數 Hoisting

2.1 使用 var 的 Hoisting

var 宣告的變數會被提升,但不會初始化,這表示變數本身會被提升到作用域頂端,但其值不會。

console.log(a); // undefined
var a = 10;
console.log(a); // 10

上述程式碼等同於:

var a;
console.log(a); // undefined

a = 10;
console.log(a); // 10

在 Hoisting 過程中,變數 a 被提升(Hoist)到了作用域的頂部,但它的值 10 並沒有一起提升,因此第一次 console.log(a); 會輸出 undefined

2.2 使用 letconst 的 Hoisting

letconst 也會被提升,但它們不會自動初始化,因此在變數宣告之前存取會導致 ReferenceError

console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 20;

等同於:

let b;
console.log(b); // ReferenceError
b = 20;

letconst 變數存在「暫時性死區(Temporal Dead Zone, TDZ)」,這表示變數在初始化之前無法被存取。

console.log(c); // ReferenceError
const c = 30;

2.3 總結變數 Hoisting

宣告方式是否 Hoisting是否初始化TDZ 存在
varundefined
let
const

3. 函式 Hoisting

3.1 Function Declaration(函式宣告)

函式宣告(function foo() {})會完整 Hoisting,因此可以在定義之前調用。

hello(); // Hello, world!

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

在執行時,JavaScript 會將函式整個提升到作用域頂部,因此 hello() 可以在函式宣告前執行。

等同於:

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

hello(); // Hello, world!

3.2 Function Expression(函式表達式)

使用 var 宣告的函式表達式(Function Expression)僅會提升變數,但不會提升函式內容,因此無法在宣告前調用。

console.log(sayHi); // undefined
sayHi(); // TypeError: sayHi is not a function

var sayHi = function() {
console.log("Hi!");
};

上述程式碼等同於:

var sayHi;
console.log(sayHi); // undefined

sayHi = function() {
console.log("Hi!");
};

因為 sayHi 在 Hoisting 時只被提升變數,但未初始化,因此 console.log(sayHi); 顯示 undefined,並且 sayHi(); 會導致 TypeError

3.3 使用 letconst 的 Function Expression

若函式表達式使用 letconst,則變數仍然會被提升,但會受到暫時性死區(TDZ)影響,因此在初始化前使用會導致 ReferenceError

console.log(sayHello); // ReferenceError: Cannot access 'sayHello' before initialization
let sayHello = function() {
console.log("Hello!");
};

4. 結論與最佳實踐

4.1 總結 Hoisting 行為

  1. 變數宣告
    • var 會被提升並初始化為 undefined
    • letconst 會被提升,但不會初始化(存在 TDZ)。
  2. 函式宣告
    • function 會完整提升,可以在宣告前調用。
    • var 宣告的函式表達式只提升變數,無法在宣告前調用。
    • letconst 宣告的函式表達式受 TDZ 影響,無法在宣告前使用。

4.2 最佳實踐

  • 避免使用 var,改用 letconst
  • 函式表達式應在使用前宣告,避免 undefinedReferenceError
  • 將所有變數與函式宣告放在作用域的開頭,可減少 Hoisting 帶來的困惑。

4.3 最佳實踐範例

// 正確做法:將變數與函式宣告放在最上方
function greet() {
console.log("Hello, world!");
}

greet();

const sayHi = function() {
console.log("Hi!");
};

sayHi();

這樣可以確保程式碼易於理解,並避免因 Hoisting 造成的問題。

參考文件

  1. [JavaScript] Javascript 的執行環境 (Execution context) 與堆疊 (Stack)
  2. 初學者指南:深入了解 JavaScript 中的 Event Loop(事件循環)
  3. 初學者指南:深入了解 JavaScript 的 Call Stack(呼叫堆疊)
  4. 初學者指南:深入了解 JavaScript 的執行環境(Execution Context)
  5. 初學者指南:深入了解 JavaScript 的建立期與執行期
  6. 初學者指南:深入了解 JavaScript 中函式與變數的建立期與執行期差異

JavaScript 入門教學筆記 | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

前言

JavaScript(簡稱 JS)是一種高階、直譯式、弱型別的程式語言,廣泛應用於 Web 開發。它最初用於瀏覽器端,現在也能在伺服器端(如 Node.js)運行,並支援多種應用開發,如網頁、行動應用、桌面應用等。


1. JavaScript 的基本語法

1.1 變數與常數

在 JavaScript 中,可以使用 varletconst 來宣告變數。

var a = 10; // 傳統變數宣告(不建議使用)
let b = 20; // 可變變數
const c = 30; // 常數,無法重新賦值

letconst 具有區塊作用域(Block Scope),而 var 則具有函式作用域(Function Scope)。


1.2 資料型別

JavaScript 主要的資料型別包括:

  • 原始型別(Primitive Types)

    • string(字串)
    • number(數字,包括整數與浮點數)
    • boolean(布林值)
    • null(空值)
    • undefined(未定義)
    • symbol(符號,ES6 引入)
  • 參考型別(Reference Types)

    • object(物件)
    • array(陣列)
    • function(函式)

範例:

let name = 'Alice'; // 字串
let age = 25; // 數字
let isStudent = true; // 布林值
let hobby = null; // 空值
let score; // 未定義(undefined)
let person = { name: 'Bob', age: 30 }; // 物件
let colors = ['red', 'green', 'blue']; // 陣列

2. 運算子

2.1 算術運算子

let x = 10;
let y = 5;

console.log(x + y); // 加法:15
console.log(x - y); // 減法:5
console.log(x * y); // 乘法:50
console.log(x / y); // 除法:2
console.log(x % y); // 餘數:0
console.log(x ** 2); // 次方運算:100

2.2 比較運算子

console.log(10 > 5); // true
console.log(10 >= 10); // true
console.log(10 < 5); // false
console.log(10 === '10'); // false(全等)
console.log(10 == '10'); // true(寬鬆比較)
console.log(10 !== '10'); // true(全不等)
console.log(10 != '10'); // false(寬鬆不等)

2.3 邏輯運算子

console.log(true && false); // false(AND)
console.log(true || false); // true(OR)
console.log(!true); // false(NOT)

3. 控制流程

3.1 條件判斷

let score = 75;

if (score >= 90) {
console.log('A');
} else if (score >= 80) {
console.log('B');
} else {
console.log('C');
}

3.2 三元運算子

let age = 18;
let canVote = age >= 18 ? '可以投票' : '不可以投票';
console.log(canVote);

3.3 switch 語法

let fruit = 'apple';

switch (fruit) {
case 'banana':
console.log('黃色');
break;
case 'apple':
console.log('紅色');
break;
default:
console.log('未知');
}

4. 迴圈

4.1 for 迴圈

for (let i = 1; i <= 5; i++) {
console.log('數字:' + i);
}

4.2 while 迴圈

let num = 1;

while (num <= 5) {
console.log('數字:' + num);
num++;
}

4.3 forEach(適用於陣列)

let colors = ['red', 'green', 'blue'];

colors.forEach(function (color) {
console.log(color);
});

5. 函式(Functions)

5.1 一般函式

function add(a, b) {
return a + b;
}

console.log(add(3, 5)); // 8

5.2 箭頭函式(Arrow Function)

const multiply = (a, b) => a * b;

console.log(multiply(4, 6)); // 24

5.3 立即執行函式(IIFE)

(function () {
console.log('這是立即執行函式');
})();

6. 陣列與物件

6.1 陣列操作

let fruits = ['apple', 'banana', 'cherry'];

fruits.push('grape'); // 新增
console.log(fruits); // ['apple', 'banana', 'cherry', 'grape']

fruits.pop(); // 移除最後一個
console.log(fruits); // ['apple', 'banana', 'cherry']

6.2 物件操作

let person = {
name: 'Alice',
age: 25,
greet: function () {
console.log('Hello, my name is ' + this.name);
}
};

console.log(person.name); // 'Alice'
person.greet(); // 'Hello, my name is Alice'

7. 非同步處理

7.1 setTimeoutsetInterval

setTimeout(() => {
console.log('這段文字將在 2 秒後出現');
}, 2000);

7.2 Promise

let fetchData = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('資料已載入');
}, 2000);
});

fetchData.then((message) => console.log(message));

7.3 async/await

async function fetchData() {
return '資料已載入';
}

fetchData().then(console.log);

總結

JavaScript 是一種靈活且功能強大的語言,適用於前端與後端開發。本文介紹了基本語法、變數、運算子、控制流程、函式、陣列、物件及非同步處理等內容,這些知識構成 JavaScript 入門的基礎。建議透過實際練習來加深理解,如使用瀏覽器開發者工具或建立小型專案來測試所學內容。