

รู้จักกับ let ใน JavaScript: พื้นฐานที่นักพัฒนาทุกคนต้องเข้าใจ
ในโลกของการพัฒนาเว็บแอปพลิเคชันด้วย JavaScript หนึ่งในแนวคิดที่สำคัญที่สุดที่นักพัฒนาต้องทำความเข้าใจคือ การประกาศตัวแปร (Variable Declaration) และหนึ่งใน keyword ที่ถูกพูดถึงมากที่สุดในยุค ES6 (ECMAScript 2015) ก็คือ let บทความนี้จะพาคุณไปสำรวจทุกแง่มุมของ let ตั้งแต่พื้นฐานไปจนถึงเทคนิคขั้นสูง พร้อมตัวอย่างการใช้งานจริงในปี 2026
let ถูกนำมาใช้เพื่อแก้ปัญหาที่เกิดขึ้นกับ var ซึ่งเป็น keyword เดิมที่มีพฤติกรรมแปลกประหลาดหลายอย่าง เช่น Hoisting ที่ทำให้ตัวแปรสามารถเข้าถึงได้ก่อนประกาศ หรือ Function Scope ที่ทำให้เกิดปัญหาในลูปและเงื่อนไขต่างๆ let จึงถูกออกแบบมาให้มี Block Scope ที่ชัดเจนและคาดเดาได้ง่ายกว่า
ความแตกต่างระหว่าง let, var และ const
ก่อนที่เราจะลงลึกในรายละเอียดของ let เราควรทำความเข้าใจความแตกต่างระหว่างตัวเลือกทั้งสามที่ JavaScript มีให้ นี่คือตารางเปรียบเทียบที่ครอบคลุม:
| คุณสมบัติ | var | let | const |
|---|---|---|---|
| Scope | Function Scope | Block Scope | Block Scope |
| สามารถ Reassign ได้ | ใช่ | ใช่ | ไม่ (ต้องกำหนดค่าเริ่มต้น) |
| สามารถ Redeclare ได้ | ใช่ | ไม่ | ไม่ |
| Hoisting | ใช่ (ได้ค่า undefined) | ใช่ (แต่เข้า Temporal Dead Zone) | ใช่ (แต่เข้า Temporal Dead Zone) |
| Temporal Dead Zone (TDZ) | ไม่มี | มี | มี |
| ใช้ใน Global Scope | กลายเป็น property ของ window | ไม่กลายเป็น property ของ window | ไม่กลายเป็น property ของ window |
ทำไมต้องเลือก let แทน var?
สาเหตุหลักที่นักพัฒนาสมัยใหม่นิยมใช้ let มากกว่า var คือ:
- Block Scope – ป้องกันการรั่วไหลของตัวแปรออกนอกบล็อกที่ควรจะเป็น
- ป้องกันการประกาศซ้ำ – ลดความผิดพลาดจากการประกาศตัวแปรชื่อเดียวกันโดยไม่ตั้งใจ
- Temporal Dead Zone – บังคับให้ประกาศตัวแปรก่อนใช้งาน ทำให้โค้ดอ่านง่ายขึ้น
- เหมาะกับลูป – แต่ละ iteration จะได้ตัวแปรใหม่ ไม่แชร์ค่าเดียวกัน
Block Scope และ Temporal Dead Zone (TDZ)
หัวใจสำคัญของ let คือ Block Scope ซึ่งหมายถึงตัวแปรที่ประกาศด้วย let จะมีชีวิตอยู่เฉพาะภายในบล็อก { } ที่มันถูกประกาศเท่านั้น ไม่ว่าจะเป็นบล็อกของ if, for, while หรือบล็อกธรรมดา
ตัวอย่าง Block Scope
{
let x = 10;
var y = 20;
console.log(x); // 10
console.log(y); // 20
}
console.log(y); // 20 (var ยังเข้าถึงได้)
console.log(x); // ReferenceError: x is not defined (let อยู่นอก scope)
Temporal Dead Zone (TDZ) คือช่วงเวลาตั้งแต่เริ่มต้น scope จนถึงจุดที่ตัวแปรถูกประกาศด้วย let หากพยายามเข้าถึงตัวแปรในช่วง TDZ จะเกิด ReferenceError ทันที ซึ่งเป็นกลไกป้องกันการใช้งานตัวแปรที่ยังไม่ได้ประกาศ
ตัวอย่าง Temporal Dead Zone
console.log(myVar); // undefined (var hoisting)
var myVar = 5;
console.log(myLet); // ReferenceError: Cannot access 'myLet' before initialization
let myLet = 10;
ในตัวอย่างข้างต้น var จะถูก hoist ไปที่ด้านบนของ scope และได้ค่า undefined ทำให้สามารถเข้าถึงได้ก่อนประกาศ ในขณะที่ let จะถูก hoist เช่นกัน แต่จะอยู่ใน TDZ จนกว่าจะถึงบรรทัดที่ประกาศจริงๆ
การใช้งาน let ในลูป for
หนึ่งในกรณีการใช้งานที่สำคัญที่สุดของ let คือในลูป for ซึ่งแตกต่างจาก var อย่างสิ้นเชิง
ปัญหาแบบเก่ากับ var
// ปัญหาคลาสสิกกับ var
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i); // พิมพ์ 5 ห้าครั้ง
}, 100);
}
// วิธีแก้แบบเก่า: ใช้ IIFE
for (var i = 0; i < 5; i++) {
(function(j) {
setTimeout(function() {
console.log(j); // 0, 1, 2, 3, 4
}, 100);
})(i);
}
วิธีที่ถูกต้องด้วย let
// ใช้ let แก้ปัญหาได้อย่างสวยงาม
for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i); // 0, 1, 2, 3, 4
}, 100);
}
// หรือใช้ Arrow Function
for (let i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 100); // 0, 1, 2, 3, 4
}
สาเหตุที่ let ทำงานได้ถูกต้องเพราะว่าในแต่ละรอบของลูป for ตัวแปร i จะถูกสร้างขึ้นใหม่ (new binding) ทำให้แต่ละ callback function ที่ถูก setTimeout จับไว้จะอ้างอิงถึงค่า i ที่แตกต่างกัน ในขณะที่ var จะใช้ตัวแปร i ตัวเดียวกันทั้งลูป
Best Practices ในการใช้ let ในปี 2026
จากประสบการณ์ของทีมพัฒนา SiamCafe และแนวโน้มของวงการ JavaScript ในปี 2026 นี่คือแนวทางปฏิบัติที่ดีที่สุด:
1. ใช้ const เป็นค่าเริ่มต้น เปลี่ยนเป็น let เมื่อจำเป็น
หลักการที่นิยมในปัจจุบันคือ “const by default, let when you must” กล่าวคือให้ใช้ const สำหรับตัวแปรที่ไม่ต้องเปลี่ยนค่า และใช้ let เฉพาะเมื่อจำเป็นต้อง reassign ค่าเท่านั้น
- ใช้
constสำหรับค่าคงที่, object, array, function - ใช้
letสำหรับตัวนับ (counter), accumulator, flag variables - หลีกเลี่ยง
varโดยเด็ดขาดในโค้ดใหม่
2. ประกาศตัวแปรให้ใกล้กับจุดที่ใช้งาน
การประกาศตัวแปร let ควรทำในขอบเขตที่แคบที่สุดเท่าที่เป็นไปได้ เพื่อลดความซับซ้อนและเพิ่ม readability
// ไม่ดี: ประกาศล่วงหน้าไกล
function processUserData(users) {
let activeUsers = [];
let totalScore = 0;
let averageScore = 0;
// ... โค้ดหลายสิบบรรทัด
for (let user of users) {
if (user.isActive) {
activeUsers.push(user);
}
}
// ดีกว่า: ประกาศเมื่อจำเป็น
let totalScore = 0;
for (let user of activeUsers) {
totalScore += user.score;
}
let averageScore = totalScore / activeUsers.length;
return { activeUsers, averageScore };
}
3. ระวัง Shadowing (การบังตัวแปร)
การประกาศตัวแปร let ชื่อเดียวกันใน scope ที่ซ้อนกันอาจทำให้เกิดความสับสน:
let name = "SiamCafe";
function showName() {
let name = "Blog"; // shadowing ตัวแปรนอก function
console.log(name); // "Blog"
}
showName();
console.log(name); // "SiamCafe" (ตัวแปรนอกไม่ถูกเปลี่ยน)
ถึงแม้ว่า shadowing จะมีประโยชน์ในบางกรณี แต่ควรใช้อย่างระมัดระวัง เพราะอาจทำให้โค้ดอ่านยากและเกิดบั๊กที่หาได้ยาก
กรณีการใช้งานจริง (Real-World Use Cases)
Use Case 1: ระบบจัดการตะกร้าสินค้า (Shopping Cart)
ในแอปพลิเคชันอีคอมเมิร์ซ การใช้ let สำหรับตัวแปรที่ต้องเปลี่ยนแปลงบ่อยๆ เป็นเรื่องปกติ:
class ShoppingCart {
constructor() {
this.items = [];
}
addItem(product, quantity = 1) {
let existingItem = this.items.find(item => item.id === product.id);
if (existingItem) {
existingItem.quantity += quantity;
} else {
this.items.push({
id: product.id,
name: product.name,
price: product.price,
quantity: quantity
});
}
this.updateUI();
}
calculateTotal() {
let total = 0;
for (let item of this.items) {
total += item.price * item.quantity;
}
// ใช้ let สำหรับส่วนลด
let discount = this.getDiscount();
if (discount > 0) {
total = total - (total * discount / 100);
}
return total;
}
getDiscount() {
// ใช้ let สำหรับตัวแปรชั่วคราว
let totalItems = this.items.reduce((sum, item) => sum + item.quantity, 0);
let discount = 0;
if (totalItems >= 10) discount = 15;
else if (totalItems >= 5) discount = 10;
else if (totalItems >= 3) discount = 5;
return discount;
}
}
Use Case 2: ระบบค้นหาแบบ Real-time
ในการพัฒนาเว็บแอปที่ต้องมีการค้นหาแบบเรียลไทม์ let มักถูกใช้ร่วมกับ async/await:
class SearchEngine {
constructor(searchInput, resultsContainer) {
this.searchInput = searchInput;
this.resultsContainer = resultsContainer;
this.debounceTimer = null;
this.searchInput.addEventListener('input', (event) => {
// ใช้ let สำหรับ debounce
clearTimeout(this.debounceTimer);
let query = event.target.value.trim();
if (query.length < 2) {
this.resultsContainer.innerHTML = '';
return;
}
this.debounceTimer = setTimeout(async () => {
try {
let results = await this.fetchResults(query);
let filteredResults = this.filterResults(results, query);
this.renderResults(filteredResults);
} catch (error) {
console.error('Search failed:', error);
this.showError('การค้นหาล้มเหลว กรุณาลองใหม่อีกครั้ง');
}
}, 300);
});
}
async fetchResults(query) {
let response = await fetch(`/api/search?q=${encodeURIComponent(query)}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
let data = await response.json();
return data.results;
}
filterResults(results, query) {
// ใช้ let สำหรับการกรองข้อมูล
let filtered = [];
let lowerQuery = query.toLowerCase();
for (let result of results) {
let title = result.title.toLowerCase();
let description = result.description.toLowerCase();
if (title.includes(lowerQuery) || description.includes(lowerQuery)) {
filtered.push(result);
}
}
return filtered;
}
renderResults(results) {
this.resultsContainer.innerHTML = '';
for (let result of results) {
let div = document.createElement('div');
div.className = 'search-result';
div.innerHTML = `
<h3>${result.title}</h3>
<p>${result.description}</p>
`;
this.resultsContainer.appendChild(div);
}
}
}
Use Case 3: ระบบจัดการผู้ใช้ (User Management)
ในระบบจัดการผู้ใช้ที่มีการกรองและเรียงลำดับข้อมูล let ช่วยให้โค้ดสะอาดและปลอดภัย:
class UserManager {
constructor(users) {
this.users = users;
}
getActiveUsersByRole(role) {
// ใช้ let สำหรับการกรองแบบหลายเงื่อนไข
let activeUsers = [];
let currentDate = new Date();
for (let user of this.users) {
let isActive = user.status === 'active';
let hasCorrectRole = user.role === role;
let isNotExpired = user.expiryDate > currentDate;
if (isActive && hasCorrectRole && isNotExpired) {
activeUsers.push(user);
}
}
// เรียงลำดับตามวันที่สมัครล่าสุด
let sortedUsers = activeUsers.sort((a, b) => {
return new Date(b.createdAt) - new Date(a.createdAt);
});
return sortedUsers;
}
generateUserReport() {
// ใช้ let สำหรับการคำนวณสถิติ
let totalUsers = this.users.length;
let activeCount = 0;
let inactiveCount = 0;
let roleCounts = {};
for (let user of this.users) {
if (user.status === 'active') {
activeCount++;
} else {
inactiveCount++;
}
// นับจำนวนผู้ใช้แยกตามบทบาท
let role = user.role;
if (roleCounts[role]) {
roleCounts[role]++;
} else {
roleCounts[role] = 1;
}
}
return {
totalUsers,
activeCount,
inactiveCount,
roleDistribution: roleCounts,
generatedAt: new Date().toISOString()
};
}
}
ข้อควรระวังและปัญหาที่พบบ่อย
1. การใช้ let ใน Switch Statement
ใน JavaScript switch statement ไม่ได้สร้าง block scope ใหม่สำหรับแต่ละ case ดังนั้นหากใช้ let ใน case ที่ไม่มี block { } จะเกิดข้อผิดพลาด:
// ผิด: จะเกิด SyntaxError
switch (value) {
case 1:
let x = 10;
break;
case 2:
let x = 20; // SyntaxError: Identifier 'x' has already been declared
break;
}
// ถูก: ใช้ block scope
switch (value) {
case 1: {
let x = 10;
console.log(x);
break;
}
case 2: {
let x = 20;
console.log(x);
break;
}
}
2. let ใน Global Scope
การประกาศ let ใน global scope จะไม่สร้าง property บน window object ซึ่งแตกต่างจาก var:
var globalVar = "I'm global";
let globalLet = "I'm also global";
console.log(window.globalVar); // "I'm global"
console.log(window.globalLet); // undefined
ข้อแตกต่างนี้สำคัญเมื่อต้องทำงานกับโค้ดที่ต้องเข้าถึง global variables ผ่าน window object เช่น ในกรณีที่ใช้ library ภายนอก
3. let ใน Try-Catch
ใน catch block ตัวแปร exception จะเป็น block scope เสมอแม้จะใช้ var:
try {
throw new Error("Something went wrong");
} catch (error) {
let message = error.message;
console.log(message); // "Something went wrong"
}
console.log(error); // ReferenceError: error is not defined
console.log(message); // ReferenceError: message is not defined
การเปรียบเทียบประสิทธิภาพ: let vs var
ในแง่ของประสิทธิภาพ let และ var มีความแตกต่างกันเล็กน้อย แต่ในทางปฏิบัติแทบจะไม่ส่งผลกระทบต่อประสิทธิภาพโดยรวมของแอปพลิเคชัน อย่างไรก็ตาม นี่คือตารางเปรียบเทียบ:
| ปัจจัย | var | let |
|---|---|---|
| เวลาประมวลผล (Execution Time) | เร็วขึ้นเล็กน้อย (~1-5%) | ช้าลงเล็กน้อยเนื่องจาก scope checking |
| การใช้หน่วยความจำ | น้อยกว่า (ไม่ต้องจัดการ TDZ) | มากกว่าเล็กน้อย (ต้อง track scope) |
| Optimization โดย Engine | ยากกว่า (function scope) | ง่ายกว่า (block scope ชัดเจน) |
| การทำ Garbage Collection | ช้ากว่าถ้าตัวแปรยังถูกอ้างอิง | เร็วกว่าเมื่อออกจาก block |
ข้อสรุปคือ: ความแตกต่างด้านประสิทธิภาพไม่ใช่เหตุผลในการเลือกใช้ สิ่งสำคัญกว่าคือความถูกต้องของโค้ดและความสามารถในการบำรุงรักษา
อนาคตของ let ใน JavaScript ปี 2026
ในปี 2026 let กลายเป็นมาตรฐานที่นักพัฒนาทุกคนคุ้นเคย การใช้งาน var ในโค้ดใหม่ถือเป็น anti-pattern และถูกตรวจสอบโดย linter ทุกตัว อย่างไรก็ตาม มีแนวโน้มใหม่ๆ ที่น่าสนใจ:
- TypeScript ครอบงำวงการ – ภาษา TypeScript ที่ใช้
letและconstเป็นหลัก กลายเป็นมาตรฐานสำหรับโปรเจกต์ขนาดใหญ่ - Pattern Matching – ES2026 นำเสนอ Pattern Matching ที่ทำงานร่วมกับ block scope ได้ดี
- บันเดิลเลอร์สมัยใหม่ – เครื่องมืออย่าง Vite, Turbopack สามารถ optimize การใช้
letได้อย่างมีประสิทธิภาพ - AI-assisted coding – เครื่องมือ AI เช่น GitHub Copilot แนะนำ
letและconstโดยอัตโนมัติ
บทสรุปสำหรับนักพัฒนาไทย
การเรียนรู้และเข้าใจ let อย่างถ่องแท้เป็นพื้นฐานสำคัญสำหรับนักพัฒนา JavaScript ทุกคน ไม่ว่าคุณจะทำงานกับเว็บแอปพลิเคชัน, Node.js backend, หรือแม้แต่ mobile app ด้วย React Native แนวคิดเรื่อง Block Scope และ Temporal Dead Zone จะช่วยให้คุณเขียนโค้ดที่ปลอดภัยและคาดเดาได้ง่ายขึ้น
สำหรับนักพัฒนาไทยที่กำลังเริ่มต้นหรือต้องการอัปเกรดทักษะ ขอแนะนำให้:
- เปลี่ยนมาใช้ let และ const ทันที – หยุดใช้
varในโค้ดใหม่ - ใช้ const เป็นค่าเริ่มต้น – แล้วเปลี่ยนเป็น
letเมื่อจำเป็นต้อง reassign - ทำความเข้าใจ TDZ – เพื่อป้องกันข้อผิดพลาดที่เกิดจากการเข้าถึงตัวแปรก่อนประกาศ
- ใช้ linter – เช่น ESLint เพื่อบังคับใช้กฎเกี่ยวกับการประกาศตัวแปร
- ฝึกเขียนโค้ดที่ใช้ block scope – เพื่อให้คุ้นเคยกับการจัดการ scope ที่ถูกต้อง
Summary
let เป็น keyword สำหรับประกาศตัวแปรใน JavaScript ที่มี Block Scope และป้องกันปัญหาต่างๆ ที่เกิดจาก var เช่น Hoisting ที่ไม่พึงประสงค์, การรั่วไหลของตัวแปรออกนอกบล็อก, และปัญหาในลูป for การทำความเข้าใจ Temporal Dead Zone (TDZ) เป็นสิ่งสำคัญเพื่อหลีกเลี่ยง ReferenceError
ในปี 2026 การใช้ let และ const ถือเป็นมาตรฐานสำหรับนักพัฒนา JavaScript ทุกคน โดยมีแนวทางปฏิบัติคือใช้ const เป็นค่าเริ่มต้น และเปลี่ยนเป็น let เฉพาะเมื่อจำเป็นต้องเปลี่ยนค่าตัวแปรเท่านั้น การเลือกใช้ let อย่างถูกต้องจะช่วยให้โค้ดของคุณปลอดภัย อ่านง่าย และบำรุงรักษาได้ดีขึ้นในระยะยาว
บทความนี้เขียนโดยทีมงาน SiamCafe Blog — แหล่งความรู้ด้านเทคโนโลยีสำหรับนักพัฒนาไทย 如果您มีข้อสงสัยหรือต้องการแบ่งปันประสบการณ์ สามารถแสดงความคิดเห็นได้ด้านล่าง หรือติดตามบทความใหม่ๆ ได้ที่ SiamCafe.com