JavaScript: มีอะไรใหม่บ้างใน ES2021
มาตรฐาน ES2021 ฉบับเต็มได้ปล่อยออกมาแล้ว มาดูกันซิว่ามีฟีเจอร์ใหม่อะไรถูกเพิ่มมาบ้าง
String.prototype.replaceAll
เมธอด replaceAll
ของ String ใช้เพื่อแทนที่คำที่ต้องการในทุกตำแหน่งที่ค้นเจอภายใต้ String ที่ระบุ
ก่อนหน้านี้หากต้องการค้นหาและแทนที่ String จะต้องใช้เมธอด replace ควบคู่กับ "g" flag ของ Regular Expression เช่น
ตัวอย่างต่อไปนี้คือการแทนที่คำว่า ซินโนแวค ด้วยคำว่า ไฟเซอร์
1const str = `2 ชาวดาวนาเมะฉีดซิโนแวคไปแล้วกว่า 99%3 วัคซีนที่ดีที่สุดในจักรวาลคือซิโนแวค4`56str.replace(/ซิโนแวค/g, 'ไฟเซอร์')
ผลลัพธ์จากการแทนที่ดังกล่าวคือ
1ชาวดาวนาเมะฉีดไฟเซอร์ไปแล้วกว่า 99%2วัคซีนที่ดีที่สุดในจักรวาลคือไฟเซอร์
สำหรับ ES2021 นั้นเราสามารถใช้เมธอด replaceAll
แทนได้ดังนี้
1str.replaceAll('ซิโนแวค', 'ไฟเซอร์')
Numeric Separator
แต่เดิมนั้นการระบุตัวเลขเราไม่สามารถใส่ตัวคั่นใด ๆ ได้ หากระบุตัวเลขหนึ่งล้านจึงต้องระบุเป็น 1000000
ซึ่งค่อนข้างยากในการอ่านค่าของเลขดังกล่าว
สำหรับ ES2021 นั้นเราสามารถใช้เครื่องหมาย underscore ในการคั่นตำแหน่งของตัวเลขได้ เช่น
1const ONE_MILLION = 1_000_0002const ONE_BILLION = 1_000_000_000
ทั้งนี้การใช้ underscore ในการคั่นตำแหน่งตัวเลขนั้น JavaScript ไม่ได้กำหนดรูปแบบการใช้งานตายตัวว่าต้องระบุตำแหน่งอย่างไร
ดังนั้นการเขียนค่าหนึ่งพันด้วย 1_000
หรือ 10_00
จึงมีค่าเท่ากัน
Promise.any
Promise.any()
รับค่าอาร์เรย์ของ Promise และคืนกลับเมื่อ Promise ตัวใดตัวหนึ่งในกลุ่มทำงานเสร็จสมบูรณ์โดยไม่มีข้อผิดพลาด (fullfilled)
พิจารณากลุ่มของ promises ดังต่อไปนี้
1const fast = new Promise((resolve) => setTimeout(() => resolve('Fast'), 100))2const faster = new Promise((resolve) => setTimeout(() => resolve('Faster'), 50))3const fastest = new Promise((resolve) =>4 setTimeout(() => resolve('Fastest'), 25)5)
พบว่า Promise ในตัวแปร fastest
ใช้เวลา resolve สั้นสุดที่ 25 มิลลิวินาที
ดังนั้นเมื่อระบุตัวแปรทั้งสามใน Promise.any()
จึงได้ค่า promise ตัวที่ resolve ไวสุดคือ fastest นั่นเอง
1const promises = [fast, faster, fastest]23// ผลลัพธ์คือ Fastest4Promise.any(promises).then((value) => console.log(value))
กรณีที่ Promise ตัวหนึ่งแม้จะใช้เวลาน้อยสุดแต่กลับมีสถานะเป็น rejected
Promise นั้นจะไม่ถือเป็นผลลัพธ์ของ Promise.any()
1const fast = new Promise((resolve) => setTimeout(() => resolve('Fast'), 100))2const faster = new Promise((resolve) => setTimeout(() => resolve('Faster'), 50))3const fastest = new Promise((_, reject) =>4 setTimeout(() => reject('Fastest'), 25)5)6const promises = [fast, faster, fastest]78// ผลลัพธ์คือ Faster9Promise.any(promises).then((value) => console.log(value))
จากโค้ดข้างต้นแม้ fastest จะใช้เวลาน้อยสุดแต่กลับไม่ได้ resolve ข้อมูลกลับมาหากแต่ reject ข้อมูลนั้นแทน
fastest จึงไม่ใช่ผลลัพธ์ของ Promise.any()
Promise ตัวถัดมาในกลุ่มที่ทำงานได้เร็วรองลงมานั่นคือ faster จึงเป็นผู้ชนะ
กรณีที่ทุก Promise ในกลุ่มล้วนมีสถานะเป็น rejected Promise.any
จะคืนข้อผิดพลาดด้วยชนิดข้อมูลคือ AggregateError
กลับมา
1const fast = new Promise((_, reject) => setTimeout(() => reject('Fast'), 100))2const faster = new Promise((_, reject) =>3 setTimeout(() => reject('Faster'), 50)4)5const fastest = new Promise((_, reject) =>6 setTimeout(() => reject('Fastest'), 25)7)8const promises = [fast, faster, fastest]910Promise.any(promises).catch((ex) => {11 // All promises were rejected12 console.log(ex.message)1314 // true15 console.log(ex instanceof AggregateError)1617 // AggregateError18 console.log(ex.name)1920 for (const error of ex.errors) {21 // Fast22 // Faster23 // Fastest24 console.log(error)25 }26})
Logical OR assignment operator
ES2021 เพิ่มตัวดำเนินการ ||=
ที่มีรูปแบบการใช้คือ x ||= y
ในความหมายเดียวกับการเขียน x || (x = y)
หากเราเขียน x ||= y
จึงหมายความว่า x จะมีค่าเดิมเมื่อ x เป็นตัวแปรที่มีค่าอยู่แล้ว (ไม่เป็น 0, false, null หรือ undefined)
แต่ x จะมีค่าใหม่เป็น y เมื่อ x ไม่มีค่า (เป็น 0, false, null หรือ undefined)
1const person = { name: 'Somchai' }23person.gender ||= 'Male'45console.log(person.gender) // Male
จากตัวอย่างข้างต้น เหตุเพราะ person.gender ไม่มีค่า ค่าของ Male ซึ่งอยู่หลังเครื่องหมาย ||=
จึงถูกกำหนดให้เป็นค่าของ person.gender แทน
เนื่องจาก x ||= y
จะได้ค่า x เดิมเมื่อ x มีค่าอยู่แล้ว จึงสามารถใช้เครื่องหมายนี้เพื่อป้องกันการคำนวณซ้ำได้
1class Admin {2 #currentUser = null34 get currentUser() {5 return (this.#currentUser ||= User.findAdmin())6 }7}89const admin = new Admin()10admin.currentUser
ตัวอย่างข้างต้นเมื่อเรียก admin.currentUser
จังหวะนั้น this.#currentUser
ยังไม่มีค่าจึงทำการเรียกใช้ User.findAdmin()
เพื่อให้ได้ค่ากลับมากำหนดให้กับ
this.#currentUser
รอบถัดไปที่เรียก admin.currentUser
อีกครั้ง ในรอบนี้ this.#currentUser
มีค่าแล้วจึงใช้ค่าเดิมที่ถูกกำหนดในรอบก่อน
จะเห็นได้ว่า User.findAdmin()
อาจเป็นการเรียกใช้ข้อมูลจากฐานข้อมูลหากถูกเรียกทุกครั้งเมื่อเข้าถึงค่า this.#currentUser
จะทำให้เสียเวลาในการติดต่อฐานข้อมูลในทุกครั้ง
การใช้ ||=
จึงช่วยให้แคบค่าข้อมูลของการทำงานก่อนหน้าเอาไว้ได้
Logical AND assignment operator
การใช้เครื่องหมาย x &&= y
นั้นมีความหมายเดียวกับ x && (x = y)
กล่าวคือเมื่อใดที่ x มีค่าแล้วค่าของ x จะเป็น y
หรือกล่าวอีกอย่างได้ว่า x &&= y
ก็เสมือนหนึ่งใช้ if ในการตรวจสอบค่า x
1if (x) {2 x = y3}
ตัวอย่างการใช้งานเช่น ในตัวแปร person ที่มีค่า fullName อยู่ หากเรากำหนดเงื่อนไขที่ว่าเมื่อมีพร็อพเพอร์ตี้ fullName ในออบเจ็กต์ person ให้ทำการแยกชื่อและนามสกุลออกจากกันเป็นพร็อพเพอร์ตี้ชื่อ firstName และ lastName ตามลำดับพร้อมทั้งทำการเปลี่ยนค่าของ fullName ให้เป็น undefined โค้ดตามเงื่อนไขข้างต้นเป็นดังนี้
1const person = {2 fullName: 'Somchai Haha',3}45const splitFullName = (person) => {6 ;[person.firstName, person.lastName] = person.fullName.split(' ')7}89person.fullName &&= splitFullName(person)
เหตุเพราะฟังก์ชัน splitFullName
ไม่คืนค่าใดกลับออกมา (คืน undefined) ผลลัพธ์การทำงานของ &&=
จึงทำให้
person.fullName ได้ค่า undefined จากฟังก์ชันนี้กำหนดให้ตนเอง
Logical Nullish assignment operator
การดำเนินการ x ??= y
ใช้พิจารณาหาก x ไม่เป็น null หรือ undefined แล้ว x จะมีค่าคงเดิม
หาก x เป็น null หรือ undefined แล้ว x จะมีค่าเป็น y x ??= y
จึงมีความหมายเดียวกับ x ?? (x = y)
ส่วนแตกต่างระหว่างการใช้ x ??= y
กับ x ||= y
คือการพิจารณาค่า x กรณีของ x ||= y
นั้น x จะมีค่าเป็น y เมื่อ
x ไม่มีค่า (x มีค่าที่แปลงเป็น boolean แล้วได้ false เช่น 0, false, null, '' หรือ undefined)
แต่กรณีของ x ??= y
จะพิจารณาค่า x ว่าเป็น null หรือ undefined หรือไม่นั่นเอง
WeakRef และ FinalizationRegistry
ในหัวข้อนี้จะขอแยกออกเป็นอีกบทความครับ
สารบัญ
- String.prototype.replaceAll
- Numeric Separator
- Promise.any
- Logical OR assignment operator
- Logical AND assignment operator
- Logical Nullish assignment operator
- WeakRef และ FinalizationRegistry