JavaScript: มีอะไรใหม่บ้างใน ES2021

Nuttavut Thongjor

มาตรฐาน ES2021 ฉบับเต็มได้ปล่อยออกมาแล้ว มาดูกันซิว่ามีฟีเจอร์ใหม่อะไรถูกเพิ่มมาบ้าง

String.prototype.replaceAll

เมธอด replaceAll ของ String ใช้เพื่อแทนที่คำที่ต้องการในทุกตำแหน่งที่ค้นเจอภายใต้ String ที่ระบุ ก่อนหน้านี้หากต้องการค้นหาและแทนที่ String จะต้องใช้เมธอด replace ควบคู่กับ "g" flag ของ Regular Expression เช่น ตัวอย่างต่อไปนี้คือการแทนที่คำว่า ซินโนแวค ด้วยคำว่า ไฟเซอร์

Code
1const str = `
2 ชาวดาวนาเมะฉีดซิโนแวคไปแล้วกว่า 99%
3 วัคซีนที่ดีที่สุดในจักรวาลคือซิโนแวค
4`
5
6str.replace(/ซิโนแวค/g, 'ไฟเซอร์')

ผลลัพธ์จากการแทนที่ดังกล่าวคือ

Code
1ชาวดาวนาเมะฉีดไฟเซอร์ไปแล้วกว่า 99%
2วัคซีนที่ดีที่สุดในจักรวาลคือไฟเซอร์

สำหรับ ES2021 นั้นเราสามารถใช้เมธอด replaceAll แทนได้ดังนี้

Code
1str.replaceAll('ซิโนแวค', 'ไฟเซอร์')

Numeric Separator

แต่เดิมนั้นการระบุตัวเลขเราไม่สามารถใส่ตัวคั่นใด ๆ ได้ หากระบุตัวเลขหนึ่งล้านจึงต้องระบุเป็น 1000000 ซึ่งค่อนข้างยากในการอ่านค่าของเลขดังกล่าว

สำหรับ ES2021 นั้นเราสามารถใช้เครื่องหมาย underscore ในการคั่นตำแหน่งของตัวเลขได้ เช่น

Code
1const ONE_MILLION = 1_000_000
2const ONE_BILLION = 1_000_000_000

ทั้งนี้การใช้ underscore ในการคั่นตำแหน่งตัวเลขนั้น JavaScript ไม่ได้กำหนดรูปแบบการใช้งานตายตัวว่าต้องระบุตำแหน่งอย่างไร ดังนั้นการเขียนค่าหนึ่งพันด้วย 1_000 หรือ 10_00 จึงมีค่าเท่ากัน

Promise.any

Promise.any() รับค่าอาร์เรย์ของ Promise และคืนกลับเมื่อ Promise ตัวใดตัวหนึ่งในกลุ่มทำงานเสร็จสมบูรณ์โดยไม่มีข้อผิดพลาด (fullfilled) พิจารณากลุ่มของ promises ดังต่อไปนี้

Code
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 นั่นเอง

Code
1const promises = [fast, faster, fastest]
2
3// ผลลัพธ์คือ Fastest
4Promise.any(promises).then((value) => console.log(value))

กรณีที่ Promise ตัวหนึ่งแม้จะใช้เวลาน้อยสุดแต่กลับมีสถานะเป็น rejected Promise นั้นจะไม่ถือเป็นผลลัพธ์ของ Promise.any()

Code
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]
7
8// ผลลัพธ์คือ Faster
9Promise.any(promises).then((value) => console.log(value))

จากโค้ดข้างต้นแม้ fastest จะใช้เวลาน้อยสุดแต่กลับไม่ได้ resolve ข้อมูลกลับมาหากแต่ reject ข้อมูลนั้นแทน fastest จึงไม่ใช่ผลลัพธ์ของ Promise.any() Promise ตัวถัดมาในกลุ่มที่ทำงานได้เร็วรองลงมานั่นคือ faster จึงเป็นผู้ชนะ

กรณีที่ทุก Promise ในกลุ่มล้วนมีสถานะเป็น rejected Promise.any จะคืนข้อผิดพลาดด้วยชนิดข้อมูลคือ AggregateError กลับมา

Code
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]
9
10Promise.any(promises).catch((ex) => {
11 // All promises were rejected
12 console.log(ex.message)
13
14 // true
15 console.log(ex instanceof AggregateError)
16
17 // AggregateError
18 console.log(ex.name)
19
20 for (const error of ex.errors) {
21 // Fast
22 // Faster
23 // Fastest
24 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)

Code
1const person = { name: 'Somchai' }
2
3person.gender ||= 'Male'
4
5console.log(person.gender) // Male

จากตัวอย่างข้างต้น เหตุเพราะ person.gender ไม่มีค่า ค่าของ Male ซึ่งอยู่หลังเครื่องหมาย ||= จึงถูกกำหนดให้เป็นค่าของ person.gender แทน เนื่องจาก x ||= y จะได้ค่า x เดิมเมื่อ x มีค่าอยู่แล้ว จึงสามารถใช้เครื่องหมายนี้เพื่อป้องกันการคำนวณซ้ำได้

Code
1class Admin {
2 #currentUser = null
3
4 get currentUser() {
5 return (this.#currentUser ||= User.findAdmin())
6 }
7}
8
9const 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

Code
1if (x) {
2 x = y
3}

ตัวอย่างการใช้งานเช่น ในตัวแปร person ที่มีค่า fullName อยู่ หากเรากำหนดเงื่อนไขที่ว่าเมื่อมีพร็อพเพอร์ตี้ fullName ในออบเจ็กต์ person ให้ทำการแยกชื่อและนามสกุลออกจากกันเป็นพร็อพเพอร์ตี้ชื่อ firstName และ lastName ตามลำดับพร้อมทั้งทำการเปลี่ยนค่าของ fullName ให้เป็น undefined โค้ดตามเงื่อนไขข้างต้นเป็นดังนี้

Code
1const person = {
2 fullName: 'Somchai Haha',
3}
4
5const splitFullName = (person) => {
6 ;[person.firstName, person.lastName] = person.fullName.split(' ')
7}
8
9person.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