ตัวแปร Let ใน Javascript ES6, Typescript
Let
ทุกคนคงทราบกันดีแล้วว่า var คือตัวแปรของ javascript มีลักษณะเป็น function scope แต่เจ้าตัว scope ของ javascript มันไม่เหมือนกับภาษาอื่นเช่นพวก C# / JAVA หรือภาษาอื่น ๆ ถ้าบอกว่า block scope ในภาษาอื่น ผลลัพธ์ของ code ตัวอย่างข้างล่างนี้ที่น่าจะได้คือ 123 แต่ javascript กลับจะได้ 456 แทน
var foo = 123;
if (true) {
var foo = 456;
}console.log(foo); // 456
เพราะว่า {} ของ javascript นั้นไม่ได้มีการสร้างตัวแปรขึ้นมาใหม่แต่ใช้พื้นที่ในหน่วยความจำเดียวกันทั้งข้างนอกและข้างใน Block ทำให้การเขียนโปรแกรมในภาษา javascript เกิดความผิดพลาดได้ง่าย TypeScript(กับ ES6) เลยสร้างชนิดตัวแปรใหม่ขึ้นมาคือ let ที่จะทำให้สามารถสร้างตัวแปรใน scope ได้ตามปกติในแบบที่ภาษาอื่นเค้าทำกัน ดังตัวอย่าง
let foo = 123;
if (true) {
let foo = 456;
}console.log(foo); // 123
วาง let ไว้ใน loop ป้องกันความผิดพลาดจากตัวแปร
var index = 0;
var array = [1, 2, 3];
for (let index = 0; index < array.length; index++) {
console.log(array[index]);
}console.log(index); // 0
เห็นได้ว่าการแทนที่ var ด้วย let ช่วยป้องกันการผิดผลาดทางด้านภาษาได้เยอะที่เดียว
ฟังก์ชั่ง javascript สามารถสร้าง scope ใหม่ได้
ฟังก์ชั่งใน Javascript ได้ทำการสร้าง scope ขึ้นมาใหม่ให้ โดยการทดลองง่าย ๆ ที่แสดงให้เห็นผลลัพธ์ดังนี้
var foo = 123;
function test() {
var foo = 456;
}test();
console.log(foo); // 123
เราได้ผลลัพธ์ตามที่คาดไว้ แต่ถ้าจะให้เขียน Code แบบนี้คงยุ่งยากมาทีเดียว
Generated JS
ภาษา Javascript ที่ถูกแปลงจาก typescript จะถูกแปลงจาก let เป็น var ตามชื่อเดิมของมันถ้าชื่อนั้นไม่มีใน scope อยู่แล้ว
if (true) {
let foo = 123;
} // แปลงเป็น //if (true) {
var foo = 123;
}
แต่ถ้ามีชื่อเดิมอยู่แล้วมันจะถูกเปลี่ยนชื่อเป็นชื่ออื่นเพื่อให้ไม่ซ้ำกับตัวเดิมใน scope
var foo = ‘123’;
if (true) {
let foo = 123;
} // แปลงเป็น //var foo = ‘123’;
if (true) {
var _foo = 123; // Renamed
}
จะเห็นได้ว่า foo ใน {} ถูกเปลี่ยนเป็น _foo ทำให้ค่าของตัวแปรทั้งสองแยกออกจากกัน
let in closures
ลองดูตัวอย่างโปรแกรมต่อไปนี้
var funcs = []; // create a bunch of functionsfor (var i = 0; i < 3; i++) {
funcs.push(function() {
console.log(i);
}
}// call themfor (var j = 0; j < 3; j++) {
funcs[j]();
}
เราคาดว่าผลลัพธ์ที่ได้จะออกมาเป็น 0,1,2 ใช่รึเปล่า ลองเล่นดูได้เลยครับ
คำตอบคือ ไม่ใช้ น่าแปลกทีค่าออกมาเป็น 3 ทั้ง 3 ครั้งเลย
เหตุผลเพราะ ทั้ง 3 function ใช้ตัวแปร i ตัวเดียวกัน เนื่องจากเราทำการ run จาก loop ที่ 2 ซึ่งค่า i มีค่าเป็น 3 ไปแล้วจาก loop แรก (ค่าใน func เป็น console.log(i) ไม่ใช่ console.log(0) console.log(1) console.log(2))
เราสามารถแก้ปัญหาได้โดยการสร้างตัวแปรเฉพาะให้แต่ละ loop เหมือนกับที่เรารู้ตอนตันแล้วว่าถ้าเราสร้างตัวแปรใน function แล้วทำการ excute ตัวแปรจะถูกสร้างเป็นคนละตัวกัน ดังโปรแกรมต่อไปนี้
var funcs = [];// create a bunch of functionsfor (var i = 0; i < 3; i++) {
(function() {
var local = i;
funcs.push(function() { console.log(local); })
})();
}// call themfor (var j = 0; j < 3; j++) {
funcs[j]();
}
ผลลัทธ์ที่ได้จะเป็น 0 1 2 ตามที่หวังไว้
** closures มีผลกับ performance เพราะต้องเก็บข้อมูลแยกกันของแต่ละช่วง
แต่ถ้าเราใช้ let แทน var จะสามารถทำได้เลยโดยที่ไม่ต้องสร้าง function มาจัดการมันอีกที
var funcs = [];// create a bunch of functionsfor (let i = 0; i < 3; i++) { // Note the use of let
funcs.push(function() { console.log(i); })
}// call themfor (var j = 0; j < 3; j++) {
funcs[j]();
}
ผลลัพธ์ที่ได้เป็นไปตามที่คาดไว้ เพราะ let ได้สร้าง var ที่แยกแต่ละตัวภายใน loop ให้เลย
สรุป
ตัวแปร let สามารถช่วยเราได้อย่างมาก ทำให้ code ของเราอ่านได้ง่ายขึ้น และลดการผิดพลาดเล็ก ๆ น้อย ๆ ของโปรแกรมได้มากขึ้น
บทความอ้างอิง : https://basarat.gitbooks.io/typescript/content/docs/let.html