, October 26, 2021

0 kết quả được tìm thấy

Có gì mới ở ES2022?


  •   8 min reads
Có gì mới ở ES2022?

ES2021/ES12 mới vừa ra mắt hồi giữa năm còn nóng hổi thì giờ đây ES2022/ES13 đã được ra mắt, thật quá nhanh quá nguy hiểm đi mà 😆. Công nghệ cập nhật và thay đổi không ngừng với tốc độ thật đáng kinh ngạc. Không chần chừ thêm nữa ta cùng nhau tìm hiểu xem ở ES2022 có gì đặc sắc nào 😋

I. Class Fields

1. Class Public Instance Fields và Private Instance Fields

Kể từ phiên bản ES2015/ES6, chúng ta có thể định nghĩa các trường bằng cách sử dụng constructor. Có một quy ước chung, đó là các trường dữ liệu không được phép truy cập từ bên ngoài các phương thức của class nếu nó có dấu gạch dưới. Tuy nhiên, điều này không ngăn người dùng sử dụng class này để truy cập chúng.

class ColorButton extends HTMLElement {
  constructor() {
    this.color = "red"
    this._clicked = false
  }
}

const button = new ColorButton()
// Public fields can be accessed and changed by anyone
button.color = "blue" 

// Curse your sudden but inevitable betrayal 
console.log(button._clicked) // Prints: false, can be accessed from the instance
button._clicked = true // Doesn't throw an error, can be read from the instance

Phần đầu tiên của đề xuất thay đổi này là cung cấp một cách để xác định rõ các trường trong một class. Thay vì chúng ta định nghĩa chúng trong hàm constructor như trước thì bây giờ ta có thể định nghĩa chúng và nếu chúng ta muốn thì có thể định nghĩa chúng trên cùng bên trong class của chúng ta, như bên dưới:

class ColorButton extends HTMLElement {
  color = "red"
  _clicked = false
}

Phần thứ hai là nó cung cấp một cách an toàn hơn để ẩn các trường thuộc nhóm private khỏi những con mắt đầy tò mò 🤣. Thay vì ta sử dụng dấu gạch dưới _ thì giờ đây ta có thể sử dụng dấu thăng # (hay dấu hashtag) để phía trước tên của các trường thuộc nhóm private để chặn bất kỳ ai cố gắng truy cập chúng từ bên ngoài class mà chúng ta định nghĩa, như ví dụ bên dưới:

class ColorButton extends HTMLElement {
  // All fields are public by default
  color = "red"

  // Private fields start with a #, can only be changed from inside the class
  #clicked = false
}

const button = new ColorButton()
// Public fields can be accessed and changed by anyone
button.color = "blue"

// SyntaxError here 
console.log(button.#clicked) // Cannot be read from outside
button.#clicked = true // Cannot be assigned a value from outside

2. Private instance methods and accessors

Một số phương thức và biến của một class là những tài nguyên quan trọng để cho class đó có thể hoạt động một cách bình thường và đúng với mục đích mà nó được định nghĩa, tuy nhiên không nên để chúng có thể được truy cập một cách vô tội vạ từ bên ngoài được.

Để có thể bảo vệ các tài nguyên này và giữ cho chúng được kiểm soát một cách nghiêm ngặt thì chúng ta có thể sử dụng các phương thức và các trình truy cập (gettersetter) thuộc nhóm private với cú pháp dấu # ở phía trước. Bạn có thể xem ví dụ bên dưới:

class Banner extends HTMLElement {
  // Private variable that cannot be reached directly from outside, but can be modified by the methods inside:

  #slogan = "Hello there!"
  #counter = 0

  // private getters and setters (accessors):

  get #slogan() {return #slogan.toUpperCase()}
  set #slogan(text) {this.#slogan = text.trim()}

  get #counter() {return #counter}
  set #counter(value) {this.#counter = value}

  constructor() {
    super();
    this.onmouseover = this.#mouseover.bind(this);
  }

  // private method:
  #mouseover() {
    this.#counter = this.#counter++;
    this.#slogan = `Hello there! You've been here ${this.#counter} times.`
  }
}

Cú pháp mới này rất hay, nó cho ta cái nhìn tổng quang về các phương thức và biến thuộc nhóm private ngay khi ta nhìn vào code. Tuy nhiên, nếu trong class có nhiều phương thức với biến thuộc nhóm private quá thì nhìn đâu cũng thấy dấu # thì có hơi rối mắt đấy 🤣.

3. Static class fields và private static methods

Các trường và các phương thức thuộc static class rất là hữu ích khi bạn chỉ muốn các trường và các phương thức nhất định chỉ tồn tại trong prototype, nhưng không phải trong mọi trường hợp của class đã cho.

Mặt khác, bạn cũng có thể muốn chỉ cho phép một số trường và phương thức này chỉ được truy cập từ bên trong class. Kể từ ES2015/ES6, chúng ta có thể xác định các trường thuộc nhóm static trong một class bằng một cách đơn giản đó là xác định các trường đó trên chính class chứa nó, kiểu như bên dưới 🤭.

class Circle {}
Circle.PI = 3.14

Ở phiên bản ES2022/ES13 này, ta có thể xác định các trường thuộc nhóm static này bằng cách khai báo nó kèm với từ khóa static.

class Circle {
  static PI = 3.14
}

Giống như trên, ta có thể sử dụng dấu # để set bất kỳ các phương thức hay các trường nào của static class trở thành private. Việc này giúp ta có thể ngăn các truy vô tội vạ từ bên ngoài class và tất nhiên thì trong class đó thì vẫn sử dụng chúng bình thường thôi 😆.

class Circle {
  static #PI = 3.14

  static #calculateArea(radius) {
    return #PI * radius * radius
  }

  static calculateProperties(radius) {
    return {
      radius: radius,
      area: #calculateArea(radius)
    }
  }

}

// Public static method, outputs {radius: 10, area: 314}
console.log(Circle.calculateProperties(10))

// SyntaxError - Private static field
console.log(Circle.PI)

// SyntaxError - Private static method
console.log(Circle.calculateArea(5))

II. Ergonomic brand checks cho Private Fields

Ở các trường thuộc nhóm public, nếu các bạn cố tình hoặc vô ý truy cập vào các trường không tồn tại trong một class, bạn sẽ nhận được kết quả là undefined. Tuy nhiên, đối với các trường thuộc nhóm private thì khác, nó sẽ ném cho bạn một exception thay vì báo lại cho bạn là undefined.

Để chắc ăn, ta có thể kiểm tra xem các trường thuộc nhóm private có tồn tại trong object đó hay không, bằng cách kiểm tra xem chúng có thể truy cập từ bên trong class mà ta dùng để khởi tạo object đó nó có ném cho ta exception nào hay không.

Tuy nhiên, cách này có một cái bất cập khá lớn đó là exception có thể là một lỗi đơn giản chẳng hạn như một getter bị lỗi khi đang truy cập đến một trường nào đó hiện có. Do đó mà ở phiên bản ES này ta có thể dùng từ khóa in  để chúng ta có thể kiểm tra xem một thuộc tính hay phương thức thuộc nhóm private có tồn tại trong class đó hay không, cụ thể như sau:

class VeryPrivate {
  constructor() {
    super()
  }

  #variable
  #method() {}
  get #getter() {}
  set #setter(text) {
    this.#variable = text
  }

  static isPrivate(obj) {
    return (
      #variable in obj && #method in obj && #getter in obj && #setter in obj
    )
  }
}

III. Chỉ số Match trong RegExp

Regular expression (RegExp) hay biểu thức chính quy cho phép chúng ta tìm kiếm các mẫu trong một đoạn string. Cả Regex.execString.matchAll đều cung cấp cho chúng ta danh sách các kết quả phù hợp.

Với Regexp.exec nó trả về cho chúng ta từng kết quả một, do đó ta cần gọi nó nhiều lần để nhận tất cả các kết quả phù hợp cho đến khi nó trả về null.

Mặt khác, String.matchAll trả lại một trình vòng lặp nơi bạn có thể lặp lại trên tất cả các kết quả phù hợp. Các kết quả này bao gồm cả chuỗi ký tự đầy đủ và chuỗi con trong ngoặc đơn đang được so khớp, chuỗi đầu vào và index dựa trên 0 của kết quả khớp. Hãy xem ví dụ sau:

const str = 'Ingredients: cocoa powder, cocoa butter, other stuff'
const regex = /(cocoa) ([a-z]+)/g
const matches = [...str.matchAll(regex)]

console.log(matches[0])
/* 	
	[0: "cocoa powder", 1: "cocoa", 2: "powder"
	index: 13
	input: "Ingredients: cocoa powder, cocoa butter, other stuff"]
*/ 

console.log(matches[1])
/* 
	[0: "cocoa butter", 1: "cocoa", 2: "butter"
	index: 27
	input: "Ingredients: cocoa powder, cocoa butter, other stuff"]
 */

Mặc dù những kết quả này chứa khá nhiều thông tin về vị trí của toàn bộ matches của dữ liệu đầu vào ban đầu, nhưng chúng lại thiếu mất thông tin liên quan đến chỉ số của các matches chuỗi con.

Bằng cách sử dụng  một flag là /d chúng ta có thể đưa ra vị trí bắt đầu và kết thúc của mỗi chuỗi con một cách phù hợp.

const str = 'Ingredients: cocoa powder, cocoa butter, other stuff'
const regex = /(cocoa) ([a-z]+)/gd
const matches = [...str.matchAll(regex)]

console.log(matches[0])
/*
	0: "cocoa powder", 1: "cocoa", 2: "powder"
	index: 13
	input: "Ingredients: cocoa powder, cocoa butter, other stuff"
	indices: [[13,25],[13,18],[19,25]] <-- here
*/

console.log(matches[1])

/*
	0: "cocoa butter", 1: "cocoa", 2: "butter"
	index: 27
	input: "Ingredients: cocoa powder, cocoa butter, other stuff"
	indices: [[27,39],[27,32],[33,39]] <-- here
*/

IV. Top-level await

Hiện tại, chúng ta chỉ biết rằng ta chỉ có thể sử dụng await trong phạm vi của các function bất đồng bộ, tức là trước khai báo function ta phải có từ khóa async thì trong function ta mới dùng await được, kiểu async - await là cặp bài trùng không thể tách rời được ý🤣.

Mọi chuyện sẽ chẳng có gì đáng để nói nếu không xảy ra trường hợp như vầy: "Ví dụ như khi chúng ta đạt đến cấp cao nhất của module và không thể sử dụng từ khóa await".

Hiện tại, ở phiên bản ES này, ta đã có thể sử dụng await ở cấp cao nhất của module và nó cũng cực kỳ là tiện khi initializing importscreating fallbacks. Bạn có thể xem ví dụ bên dưới:

/* Before the top-level await, JavaScript would have given you a SyntaxError
with this line of code, but that is no more
*/
await Promise.resolve(console.log('🎉'));
// → SyntaxError: await is only valid in async function

//We have to code like this to have the correct syntax
(async function() {
  await Promise.resolve(console.log('🎉'));
  // → 🎉
}());

//Now other than await standing alone it also works properly.
await Promise.resolve(console.log("🎉"))

Vì vậy, cho đến khi lời hứa đang chờ được giải quyết, việc thực thi các module hiện tại và module mẹ nhập module con hiện tại sẽ bị hoãn lại, nhưng các module anh em có thể được thực thi theo cùng một thứ tự.

V. Tổng kết

Trên đây là một vài những thay đổi đáng kể mà ES2022/ES13 mang lại cho cộng đồng JavaScript dev chúng ta. Nếu bạn vẫn tò mò việc nó thay đổi và bổ sung những gì nữa thì bạn có thể tham khảo tại đây. Cảm ơn bác bạn đã đọc 🤗

Bài viết liên quan

Hướng dẫn clone instagram với React JS và Firebase phần 2.

Trong phần 1 của series này, chúng ta tiến hành setup một project ReactJS bằng câu lệnh create-react-app,đăng ký và tạo app trên firebase để có thể sử dụng và lưu trữ data. Trong phần 2 này, chúng ta sẽ tạo header cho nó và tạo component post để hiển thị bài đăng nhé 😉. Nào bắt đầu thôi 😁!...

Hướng dẫn clone instagram với React JS và Firebase phần 2.
Hướng dẫn clone instagram với React JS và Firebase phần 1.

Trong bài viết này, chúng ta sẽ cùng nhau clone một trang mạng xã hội nỗi tiếng, là nơi chia sẽ hình ảnh và video của hàng trăm triệu người trên toàn thế giới, đó chính là Instagram. Chúng ta bắt đầu thôi 😁!...

Hướng dẫn clone instagram với React JS và Firebase phần 1.
Number, string, array và object trong JavaScript

Chúng ta đã cùng nhau tìm hiểu các kiểu dữ liệu có trong JavaScript ở bài trước, tuy nhiên chúng ta vẫn chưa biết cách thao tác với chúng như thế nào trong JavaScript, nó có hổ trợ các phương thức nào hay không? Vấn đề đó, chúng ta sẽ cùng nhau tìm hiểu trong bài này nhé 😉....

Number, string, array và object trong JavaScript
Tìm hiểu thêm về Window Object trong Javascript

Nếu các bạn đã xem các bài trước thuộc series JavasScript cơ bản của mình hoặc đã xem các tài liệu khác thì chắc hẵn đã từng thấy trong bài viết có đề cập đến ông thần window object rồi nhỉ 😉. Vậy trong bài này, chúng ta sẽ cùng nhau tìm hiểu về nó để xem window object là loại object như thế nào nhé...

Tìm hiểu thêm về Window Object trong Javascript
Tìm hiểu về "this" trong JavaScript.

Chắc hẵn bạn đã từng nhìn thấy từ this trong một ngôn ngữ lập trình nào trước đó hoặc là trong một vài đoạn code nào đó của JavaScript. Bạn thắc mắc nó có tác dụng gì hoặc là bạn đang mơ hồ về this trong JavaScript. Yên tâm, trong bài này chúng ta sẽ cùng nhau tìm hiểu this có ý nghĩa gì nhé....

Tìm hiểu về "this" trong JavaScript.
You've successfully subscribed to 200Lab Blog
Great! Next, complete checkout for full access to 200Lab Blog
Xin chào mừng bạn đã quay trở lại
OK! Tài khoản của bạn đã kích hoạt thành công.
Success! Your billing info is updated.
Billing info update failed.
Your link has expired.