ตัวแปร Let ใน Javascript ES6, Typescript

Narupon Tee Srisantitham
Konoe
Published in
2 min readJan 9, 2017

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

--

--