JavaScript
Functions
Scope (global / local)
เรียนรู้ว่า Scope กำหนดขอบเขตการมองเห็นตัวแปรอย่างไร ทำความเข้าใจ global scope, function scope, block scope และ scope chain
Scope คือขอบเขตที่ตัวแปรถูกมองเห็น
Scope คือขอบเขตในโค้ดที่ตัวแปรตัวหนึ่งสามารถถูกเข้าถึงหรือใช้งานได้ คิดง่าย ๆ เหมือนกล่อง — ตัวแปรที่อยู่ในกล่อง มองเห็นได้จากข้างในกล่องเท่านั้น ถ้าพยายามอ่านจากข้างนอก จะหาไม่เจอ Scope ใน JavaScript มี 3 ระดับหลัก: **Global Scope**, **Function Scope**, และ **Block Scope**
`message` ถูกประกาศในฟังก์ชัน จึงใช้ได้เฉพาะในฟังก์ชันนั้น
function sayHello() {
const message = "สวัสดี";
console.log(message); // "สวัสดี"
}
sayHello();
console.log(message); // ReferenceError: message is not definedจุดสำคัญ: `message` ไม่ได้หายไป — มันแค่ถูกจำกัดให้ใช้ได้เฉพาะในฟังก์ชัน `sayHello` เท่านั้น
Global Scope — ใช้ได้จากทุกที่ แต่ควรใช้เท่าที่จำเป็น
Global Scope คือ scope ด้านนอกสุด — ตัวแปรที่ประกาศตรงนี้ (นอกทุกฟังก์ชันและบล็อก) จะมองเห็นได้จากทุกจุดในโค้ด สะดวกเมื่อหลายฟังก์ชันต้องใช้ค่าร่วมกัน แต่ถ้าใช้มากเกินไปจะทำให้โค้ดตามยาก เพราะฟังก์ชันไหนก็แก้ค่าเดียวกันได้
`appName` ประกาศนอกฟังก์ชัน — `showHeader()` เข้าถึงได้โดยไม่ต้องส่งเป็น parameter
const appName = "Learn JavaScript";
function showHeader() {
return "บทเรียน: " + appName;
}
console.log(showHeader()); // "บทเรียน: Learn JavaScript"- ใช้ global สำหรับค่าที่หลายฟังก์ชันต้องแชร์ร่วมกันจริง ๆ
- หลีกเลี่ยง global เมื่อข้อมูลใช้แค่ชั่วคราวในฟังก์ชันเดียว
- ยิ่งมีตัวแปร global มาก โอกาสชื่อชนหรือถูกแก้จากหลายจุดก็ยิ่งมาก
Function Scope (Local Scope) — สำหรับข้อมูลเฉพาะของฟังก์ชัน
ตัวแปรที่ประกาศภายในฟังก์ชันจะอยู่ใน **function scope** — มองเห็นได้เฉพาะภายในฟังก์ชันนั้น ฟังก์ชันหนึ่ง ๆ จะมองเห็นทั้ง global scope และตัวแปรของตัวเอง แต่โลกภายนอกจะไม่เห็นตัวแปรภายในฟังก์ชัน
`version` อยู่ในฟังก์ชัน — ใช้ข้างนอกไม่ได้
const appName = "MyApp"; // global
function showApp() {
const version = "1.0.0"; // local
return appName + " v" + version;
}
console.log(showApp()); // "MyApp v1.0.0"
console.log(appName); // "MyApp"
console.log(version); // ReferenceError: version is not defined**กฎง่าย ๆ**: ถ้าค่าอะไรไม่จำเป็นต้องถูกใช้จากหลายจุด ให้เริ่มจากเก็บไว้ในฟังก์ชันก่อน — scope ที่แคบกว่า = โค้ดที่ปลอดภัยกว่า
Block Scope — `let` และ `const` ป้องกันค่าหลุดออกนอกบล็อก
`{ }` ของ `if`, `for`, หรือ block เปล่า ๆ ก็สร้าง scope ได้ `let` และ `const` จะยึดติดกับ block นี้ — ตัวแปรจะใช้ได้เฉพาะใน `{ }` นั้น ส่วน `var` จะทะลุออกไปนอก block ได้ ซึ่งเป็นต้นเหตุของ bug ที่คาดไม่ถึง
| keyword | Function Scope | Block Scope |
|---|---|---|
| var | ✅ | ❌ (ทะลุออกนอก block) |
| let | ✅ | ✅ |
| const | ✅ | ✅ |
สังเกตว่า `let` และ `const` ใช้หลัง `if` block ไม่ได้ แต่ `var` ใช้ได้
// let และ const — ใช้ได้เฉพาะใน block
if (true) {
let blockVar = "อยู่ใน block";
const blockConst = "ก็อยู่ใน block";
console.log(blockVar); // "อยู่ใน block"
}
// console.log(blockVar); // ReferenceError
// var — หลุดออกมานอก block
if (true) {
var leakyVar = "หลุดออกมาได้";
}
console.log(leakyVar); // "หลุดออกมาได้"**สรุป**: ใช้ `const` หรือ `let` เสมอในโค้ดใหม่ — หลีกเลี่ยง `var` เพราะ block scope ของมันไม่ทำงานตามที่เราคาด
Scope Chain — ไล่หาจากใกล้ตัวออกไปเรื่อย ๆ
เมื่อ JavaScript ต้องการค่าของตัวแปร มันจะมองหาใน scope ปัจจุบันก่อน ถ้าไม่เจอก็ไล่ขึ้นไปยัง scope ด้านนอกทีละชั้น จนกว่าจะเจอหรือจนถึง global scope พฤติกรรมนี้เรียกว่า **Scope Chain** — inner function มองเห็นตัวแปรของ outer function และ global ได้เสมอ
`getDistrict()` อ่าน `district` จากตัวเอง, `city` จาก `getCity()`, และ `country` จาก global
const country = "ไทย"; // global
function getCity() {
const city = "กรุงเทพ";
function getDistrict() {
const district = "บางรัก";
return district + ", " + city + ", " + country;
}
return getDistrict();
}
console.log(getCity()); // "บางรัก, กรุงเทพ, ไทย"ลำดับการหา: `district` จากฟังก์ชันด้านใน → `city` จากฟังก์ชันชั้นนอก → `country` จาก global scope
ข้อผิดพลาดที่พบบ่อยกับ Scope
| สถานการณ์ | ผลที่เกิดขึ้น | วิธีแก้ |
|---|---|---|
| อ่านตัวแปร `let`/`const` นอก block | ReferenceError | ประกาศตัวแปรใน scope ที่ต้องการใช้ หรือส่งค่ากลับออกมา |
| ใช้ `var` ใน `if` หรือ `for` | ตัวแปรหลุดออกมานอก block | เปลี่ยนเป็น `let` หรือ `const` |
| ประกาศตัวแปรชื่อซ้ำใน inner scope | ชื่อด้านในบังชื่อด้านนอก (shadowing) | ใช้ได้เมื่อตั้งใจ แต่ถ้าสับสนให้เปลี่ยนชื่อให้ต่างกัน |
| ลืมว่าตัวแปรในฟังก์ชันถูกสร้างใหม่ทุกครั้งที่เรียก | ค่าจากครั้งก่อนไม่ถูกจำ | ถ้าต้องการจำค่าระหว่างการเรียก ใช้ global หรือ closure (บทถัดไป) |
**สิ่งที่ควรจำจากบทนี้**: ประกาศตัวแปรให้ใกล้จุดใช้งานที่สุด ใช้ `const` เป็นค่าเริ่มต้น และไว้ใจว่า JavaScript จะป้องกันไม่ให้ตัวแปรหลุดออกนอก scope โดยอัตโนมัติ — ตราบใดที่คุณใช้ `let`/`const`