JavaScript: มีอะไรใหม่บ้างใน ES8 (ES2017)
ES8 หรือ ES2017 พึ่งคลอดอย่างเป็นทางการเมื่อไม่นานนี้ ทว่าการอัพเกรดเวอร์ชันครั้งนี้ไม่ได้มีอะไรหวือหวาชนิดที่ต้องร้องกรี๊ดจนตับไหม้ นั่นเพราะฟีเจอร์หลักๆใน ES8 เราๆท่านๆก็ใช้กันเป็นปกติกับวุ้นแปลภาษาสุดวิเศษที่ชื่อว่า Babel กันอยู่แล้ว คิดอะไรไม่ออกจับ Babel ใส่ stage-0
โลด... แล้วคุณจะได้ทุกอย่างที่ต้องการ
แม้ ES8 จะไม่ได้ฟรุ้งฟริ้งแต่ผมก็จำเป็นต้องเขียน ถ้าไม่เขียนเรื่องนี้แล้วจะเอาเรื่องไหนมาเขียน ฮือๆ บทความหมดสต๊อกแล้ว
Object.values และ Object.entries
อ็อบเจ็กต์ใน JavaScript ประกอบด้วยส่วนที่เป็น property
(key) และ value
เช่น
1const obj = {2 a: 1,3 b: 2,4 c: 3,5}
เราทราบกันดีว่าหากเราต้องการดึงค่าของ property ทั้งหมดของอ็อบเจ็กต์ออกมา เราสามารถใช้งาน Object.keys()
ได้ เช่น
1Object.keys(obj) // ['a', 'b', 'c']
หากสถานการณ์เปลี่ยนไป เมื่อตอนนี้เราต้องการค่าของทุกๆ value เราจะสามารถเข้าถึงได้อย่างไร? ใน ES8 จึงเตรียม Object.values()
เพื่อให้เราสามารถดึงทุกค่า value ออกจากอ็อบเจ็กต์ได้ผลลัพธ์กลับในรูปอาร์เรย์
1Object.values(obj) // [1, 2, 3]
การใช้งานทั้ง Object.values
และ Object.keys
มีสิ่งนึงที่ต้องคำนึงถึงเป็นพิเศษ นั่นคือทั้งสองเมธอดสามารถเข้าถึงได้เฉพาะ property ที่ตั้งค่า enumerable
เป็น true ไว้เท่านั้น
1let obj = {}23// สร้าง property ขึ้นมาสามตัวคือ foo, bar, zoo4// โดยให้เป็น property ของ obj5Object.defineProperties(obj, {6 foo: {7 value: 'foo value',8 enumerable: true,9 },10 bar: {11 value: 'bar value',12 enumerable: true,13 },14 zoo: {15 value: 'zoo value',16 // หากไม่กำหนด enumrable จะถือว่าเป็น false17 },18})1920// เพราะว่า zoo มี enumerable เป็น false21// ค่าข้อมูลจึงไม่โผล่ในผลลัพธ์ของการเรียกใช้งาน22Object.keys(obj) // ['foo', 'bar']23Object.values(obj) // ['foo value', 'bar value']
นอกจาก Object.values
แล้ว ES8 ยังเพิ่ม Object.entries
เพื่อคืนค่าอ็อบเจ็กต์ออกมาในรูปของ [[key1, value1], [key2, value2], ..., [keyn, valuen]]
1Object.entries(obj) // [['a', 1], ['b', 2], ['c', 3]]
Trailing commas ในรายการพารามิเตอร์ของฟังก์ชัน
Trailing comma นั้นคือเครื่องหมายคอมมาที่ต่อตูดเป็นตัวสุดท้าย ใน JavaScript เวอร์ชันก่อนหน้านี้เราสามารถใช้ trailing comma ควบคู่กับอ็อบเจ็กต์และอาร์เรย์ได้ดังนี้
1const arr = [2 'Foo Foo Foo Foo',3 'Bar Bar Bar Bar',4 'Zoo Zoo Zoo Zoo', // << นี่ไงคอมมาตัวสุดท้าย5]67const obj = {8 foo: 'Foo Foo Foo Foo',9 bar: 'Bar Bar Bar Bar',10 zoo: 'Zoo Zoo Zoo Zoo', // << เค้าอยู่นี่ แหมทำเป็นมองไม่เห็น11}
คำถามคือการไม่ใส่ trailing comma ดูยังไงก็สวยกว่า แล้วแบบนี้เราจะใส่มันไปทำกระเพาะแพะรึ?
Trailing comma นั้นมีประโยชน์ครับ โดยเฉพาะกับการทำ version control สมมติว่าก่อนหน้านี้ไอ้เจ้าก้อนอาร์เรย์ของเรามีลักษณะเป็นดังนี้
1const arr = [2 'Foo Foo Foo Foo',3 'Bar Bar Bar Bar', // << trailing comma ตูอยู่หนายย~4]
จากโค้ดข้างต้นเราไม่ได้ใส่ trailing comma เอาไว้ สามวันถัดมาเราเพิ่มข้อมูลเข้าไปในอาร์เรย์นี้ แล้วทำการ push ขึ้น Git หากเราเปิด Github ขึ้นมาดูเราก็จะเจอว่า Github ได้ไฮไลต์บรรทัดที่สองของเราเพื่อเป็นการบอกว่าเรามีการแก้ไขโค้ด
1const arr = [2 'Foo Foo Foo Foo',3 'Bar Bar Bar Bar', // เพื่อเพิ่มบรรทัดที่ 3 เราต้องใส่ , เข้าไป Github ก็จะมองว่าเรามีการแก้ไขโค้ดตรงนี้จึงทำไฮไลต์ให้4 'Zoo Zoo Zoo Zoo', // นี่คือโค้ดใหม่ที่เราเพิ่มเข้ามา5]
เมื่อเราเปรียบเทียบโค้ดจำนวนมากอาจปวดหัวได้กับการไฮไลต์ของเครื่องมือ Git เหล่านี้ ปัญหานี้จะหมดไปเมื่อคุณใส่ trailing comma ทุกครั้ง นั่นเพราะทุกครั้งที่เพิ่มไอเทมใหม่ คุณไม่ต้องใส่คอมมาเข้าไปก่อนหน้า Git จึงไม่ทำไฮไลต์แจ้งว่ามีการแก้ไขในบรรทัดบน
เมื่อ trailing comma มีประโยชน์แล้วใยเราจึงไม่เพิ่มไวยากรณ์เพื่ออนุญาตให้ใช้ trailing comma กับรายการพารามิเตอร์ได้ด้วย และนี่คือสิ่งที่ ES8 เตรียมไว้ให้
1function foo(2 foo1,3 foo2,4 foo3,5 foo4,6 foo5,7 foo6,8 foo7, // ต๊ะเอ๋9)1011foo(foo1, foo2, foo3, foo4, foo5, foo6, foo7,)
Async Functions
หลายคนคงจะเบื่อกับการใช้งาน Promise เพราะขี้เกียจมานั่ง then
ยาวไปยาวไป
1const doAsync = () => {2 return new Promise((resolve, reject) => {3 setTimeout(() => {4 if (Math.random() >= 0.5) resolve('BabelCoder!')5 else reject(new Error('Less than 0.5!'))6 }, 2000)7 })8}910doAsync()11 .then((text) => {12 console.log(text)13 })14 .catch((error) => {15 console.error(error.message)16 })
นาทีนี้ Async/Await เป็นพระเอกขี่ม้าขาวที่ป็อบปูล่ามาก ที่จะมาช่วยซับน้ำตาขา Promise ให้เขียนโปรแกรมได้ลื่นไหลเสมือนนึงทำงานแบบ Synchronous แม้แต่ Node.js ก็ยังซัพพอร์ตเป็นที่เรียบร้อย แล้วมีหรือที่ ES8 จะไม่ปล่อยของออกมาซักที ตอนนี้ Async/Await เป็นมาตรฐานอย่างเต็มตัวใน ES8 แล้วแจ้
สำหรับเพื่อนๆที่รู้สึกเบาหวิวในสมองและสับสนว่า Async/Await คืออะไร เชิญเสพ บทความนี้ โดยพลัน!
Object.getOwnPropertyDescriptors
เวลาที่เราสร้าง property ขึ้นมาซักตัวในอ็อบเจ็กต์ แม้เราจะเห็นว่ามีแต่ key และ value เท่านั้นที่เราเขียนเป็นโค้ด แต่ความจริงแล้ว property แต่ละตัวมีการตั้งค่าที่มากกว่านั้น เช่น หากเราไม่ต้องการให้ property นั้นถูกเขียนค่าใหม่ได้ เราต้องเซ็ต writable
ให้เป็น false
1// แม้เราจะสร้าง property ชื่อ foo และ bar อย่างเรียบง่าย2let obj = { foo: 1, bar: 2 }34// แต่ความเป็นจริง foo ยังมีการตั้งค่าเป็นแบบนี้5{6 "configurable": true,7 "enumerable": true,8 "value": 1,9 "writable": true10}
เราสามารถกำหนดการตั้งค่าของ property เหล่านั้นได้ผ่านทาง Object.defineProperty
และ Object.defineProperties
เช่น
1let obj = {}23Object.defineProperties(obj, {4 foo: {5 value: 'foo value',6 enumerable: true,7 },8 bar: {9 value: 'bar value',10 enumerable: true,11 },12 zoo: {13 value: 'zoo value',14 },15})
เมื่อเราสามารถตั้งค่า property ได้เราก็ควรสอบถามภายหลังได้เช่นกันว่าการตั้งค่าเหล่านั้นเป็นเช่นไร ES2015 มาพร้อมกับ Object.getOwnPropertyDescriptor
ที่จะทำให้เราทราบว่า property ตัวที่ระบุมีการตั้งค่าไว้อย่างไรบ้าง
1let obj = {}23Object.defineProperties(obj, {4 foo: {5 value: 'foo value',6 enumerable: true7 },8 bar: {9 value: 'bar value',10 enumerable: true11 },12 zoo: {13 value: 'zoo value'14 }15})1617Object.getOwnPropertyDescriptor(obj, 'foo')1819// ผลลัพธ์20Object {21 "configurable": false,22 "enumerable": true,23 "value": "foo value",24 "writable": false25}
ช่างน่าขายหน้ายิ่งนัก ES2015 อนุญาตให้เราเรียกดูการตั้งค่าได้จากทีละ property ด้วยเหตุนี้ ES2017 (ES8) จึงเพิ่ม Object.getOwnPropertyDescriptors(obj)
ขึ้นมา เพื่อให้เราสามารถเรียกดูการตั้งค่าของทุกๆ properties ในอ็อบเจ็กต์นั้นได้ในคราวเดียว
1let obj = {}23Object.defineProperties(obj, {4 foo: {5 value: 'foo value',6 enumerable: true7 },8 bar: {9 value: 'bar value',10 enumerable: true11 },12 zoo: {13 value: 'zoo value'14 }15})1617Object.getOwnPropertyDescriptors(obj)1819// ผลลัพธ์20Object {21 "bar": Object {22 "configurable": false,23 "enumerable": true,24 "value": "bar value",25 "writable": false26 },27 "foo": Object {28 "configurable": false,29 "enumerable": true,30 "value": "foo value",31 "writable": false32 },33 "zoo": Object {34 "configurable": false,35 "enumerable": false,36 "value": "zoo value",37 "writable": false38 }39}
String Padding
สมมติว่าเรามีกลุ่มของราคาสินค้า แน่นอนว่าเราสามารถพิมพ์ค่าของพวกมันออกมาดูได้
1const prices = ['1,000,000', '999', '38,900', '64,111']23prices.forEach((price) => console.log(price))45// ผลลัพธ์6;('1,000,000')7;('999')8;('38,900')9;('64,111')
ช่างบัดซบเสียนี่กระไร เราอยากให้การพิมพ์ผลลัพธ์ของเรานั้นชิดขวาต่างหาก แบบนี้ไงๆ
1' 1,000,000'2' 999'3' 38,900'4' 64,111'
ES8 รู้ใจคุณจึงเตรียม padStart
และ padEnd
ไว้ให้ จากตัวอย่างก่อนหน้านี้คุณเพียงใช้ padStart
ปัญหาชีวิตรักก็จะจบสิ้น
1const prices = ['1,000,000', '999', '38,900', '64,111']23prices.forEach((price) => console.log(price.padStart(10)))
padStart
และ padEnd
นั้นเราต้องระบุความยาวเข้าไป string ใหม่ที่เป็นผลลัพธ์จะมีความยาวตามที่ระบุ หากความยาวที่ระบุยาวกว่า string ต้นฉบับ JavaScript จะทำการเติมช่องว่างเข้าไปให้จนครบความยาวที่กำหนด โดยกรณีของ padStart
จะเป็นการเติมช่องว่างเข้าข้างหน้า ในทางกลับกัน padEnd
ก็จะเติมช่องว่างเข้าประตูหลังนั่นเอง อ้า~ ฟิน... ไอ้บ้า!
1'babel coder'.padStart(15) // " babel coder"2'babel coder'.padEnd(15) // "babel coder "
ในกรณีที่ช่องว่างไม่ใช่สิ่งที่เราต้องการ เราสามารถระบุสิ่งอื่นได้ด้วยการระบุเข้าไปเป็นอาร์กิวเมนต์ตัวที่สอง
1'babel coder'.padEnd(15, '*') // "babel coder****"
ใส่ดอกจัน (เอาน้ำแข็งมาถูหลัง) แล้วให้ความรู้สึกเหมือนเซ็นเซอร์คำหยาบ 18+ เลยแฮะ~
Shared memory และ atomics
เรื่องนี้ข้ามไปเลยเพราะคงได้พูดกันยาวแน่ๆ ขอแปะโป้งไว้กล่าวถึงในบทความอื่นจะดีกว่า แต่ถ้าใครใจร้อน บทความนี้คือคำตอบของทุกสรรพสิ่งจ้า
สรุป
ES2017 หรือ ES8 ก็เหมือนการอัพเดทรายปี ความหวือหวาก็มีอยู่เช่น Async/Await แต่เพราะ Babel ที่เป็นตัวทำลายทุกโลกธาตุ ทำให้เราๆท่านๆนักพัฒนารู้สึกว่าการมาของ ES8 นั้นหน่อมแน้ม เพราะหลายๆฟีเจอร์ข้าก็ใช้ผ่าน Babel อยู่แล้ว พูดอีกก็ถูกอีก แม้ ES8 จะอุบัติขึ้นบนจักรวาลแล้ว แต่เราก็ยังคงต้องใช้ Babel หรือ TypeScript กันต่อไป นั่นเพราะฟีเจอร์หลายๆอย่างก็ยังคงไม่ได้รับการซัพพอร์ตในบราวเซอร์ทั่วๆไป (โดยเฉพาะเจ้าเก่าอย่าง IE/Edge และเจ้าใหม่ผู้เรื่องมากอย่าง Safari)
เอกสารอ้างอิง
Dor Moshe (2017). ES8 was Released and here are its Main New Features. Retrieved July, 12, 2017, from https://hackernoon.com/es8-was-released-and-here-are-its-main-new-features-ee9c394adf66
Dr. Axel Rauschmayer (2017). ES proposal: Shared memory and atomics. Retrieved July, 12, 2017, from http://2ality.com/2017/01/shared-array-buffer.html
สารบัญ
- Object.values และ Object.entries
- Trailing commas ในรายการพารามิเตอร์ของฟังก์ชัน
- Async Functions
- Object.getOwnPropertyDescriptors
- String Padding
- Shared memory และ atomics
- สรุป
- เอกสารอ้างอิง