Latest Post

Tăng thứ hạng và truy cập tự nhiên với 10 phương pháp SEO hay nhất Kiếm Tiền Online (mmo): Khái Niệm và Các Hình Thức Phổ Biến
Như mọi khi, tôi lại đem đến cho các bạn một chủ đề mới về Frontend. Có thể sau khi đọc tiêu đề, bạn đã đoán được đề tài mà tôi sẽ giới thiệu hôm nay là về ES2021. Hãy cùng tìm hiểu nhé!

ES2021 là gì?

Còn được biết đến với tên gọi ES12, JavaScript đã chính thức ra mắt với các đặc tả vô cùng chi tiết được đề xuất bởi TC39. Đối với những người chưa hiểu rõ về TC39, ta có thể tạo một “phần giới thiệu” như sau:

"ECMA International’s TC39 is a group of JavaScript developers, implementers, academics, and more, collaborating with the community to maintain and evolve the definition of JavaScript." - TC39.es

Có một điều chắc chắn răng, mỗi năm ta sẽ lại có 1 phiên bản mới của Javascript với những tính năng mới, và với vai trò là những lập trình viên “mặt tiền” thì chúng ta không thể không cập nhật chúng một cách nhanh nhất. Đúng không cả nhà?

ES2021 là gì?

Các toán tử gán có điều kiện

Vâng, các toán tử điều kiện quen thuộc của chúng ta ||, &&, ?? nay như “hổ mọc thêm cánh” (nhưng mà nằm ngang): ||=, &&=, ??=  .Giờ đây bạn có thể dùng chúng để vừa kiểm tra điều kiện mà vừa trực tiếp gán giá trị rồi nhé!

Hãy cùng điểm qua một số ví dụ ngắn sau để hiểu liền các “em nó” nào:

/*
 * "Or Or Equals"
 * Nếu x có giá trị "falsy" (false, 0...) , thực hiện toán tử gán phía bên phải
 */
x ||= y;
x || (x = y); 

/*
 * "And And Equals"
 * Nếu x có giá trị "truthy" (true, 1,...), thực hiện toán tử gán phía bên phải
 */
x &&= y;
x && (x = y);

/*
 * "Q Q Equals"
 * Nếu x không có giá trị (undefined, null,...), thực hiện toán tử gán phía bên phải
 */
x ??= y;
x ?? (x = y);
const updateID = user => {

  // Ta có thể thực hiện thủ công như này
  if (!user.id) user.id = 1

  // Hoặc như này
  user.id = user.id || 1

  // Hoặc dùng toàn tử gán có điều kiện
  user.id ||= 1
}
function setOpts(opts) {
  opts.cat ??= 'meow'
  opts.dog ??= 'bow';
}

const obj = {}
setOpts(obj)

console.log(obj) // "{ cat: 'meow', dog: 'bow' }"

Sử dụng _ để phân chia số

tính năng này cũng vô cùng thú vị, mang lại sự thuận tiện đặc biệt cho việc đọc mã nguồn. Mặc dù không tạo ra ảnh hưởng lớn đối với ứng dụng hay người dùng cuối, nhưng đối với các nhà phát triển, đây chắc chắn là một tính năng rất đặc biệt và hữu ích.

const billion = 1000_000_000;
console.log(billion); // 1000000000

Chơi luôn cả BigInt nhé, sợ gì :

const trillion = 1000_000_000_000n;
console.log(trillion.toString()); // "1000000000000"

Promise.any và AggregateError

Ồ, đây cũng là 1 tính năng hấp dẫn, Promise.any sẽ resolve ngay khi một trong các promises được cung cấp được resolve. Ví dụ cho dễ hiểu nha, ta sẽ có 3 promises với thời gian resolve là khác nhau:

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("A"), Math.floor(Math.random() * 1000));
});
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("B"), Math.floor(Math.random() * 1000));
});
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("C"), Math.floor(Math.random() * 1000));
});

Trong 3 promises p1, p2, p3, cái nào resolve trước sẽ được xử lý bởi Promise.any:

(async function () {
  const result = await Promise.any([p1, p2, p3]);
  console.log(result); // Prints "A", "B" or "C"
})();

Đương nhiên rồi, đôi khi “hứa nhiều” lại thành “hứa suông”, trong tình huống không có bất kì promise nào được thực hiện thành công, Promise.any sẽ throws ra lỗi AggregateError. Công việc của ta đó là xử lý hậu hoạ của những lời “hứa suông” đó đơn giản và nhanh gọn:

const p = new Promise((resolve, reject) => reject());

try {
  (async function () {
    const result = await Promise.any([p]);
    console.log(result);
  })();
} catch (error) {
  console.log(error.errors);
}

Để rõ hơn thì mình có chuẩn bị sẵn một quả “cap màn hình” vụ “thất hứa” quen thuộc đó cho các bạn coi đây:

Thử nghĩ mà xem, với Promise.any, ta có thể có tình huống là gọi nhiều APIs một lúc nhưng đều nhằm lấy cùng một tập dữ liệu, API nào có phản hồi sớm hơn thì ta chọn luôn cái đó… Các bạn xem còn có tình huống thú vị nào nữa không nha?

Phương thức replaceAll của String

Mình cá là có rất nhiều bạn đã gặp trường hợp muốn replace toàn bộ một chuỗi con nào đó trong một String, sau đó lên stackoverflow search thì gặp được gợi ý sử dụng phương thức replaceAll này.

Đây hẳn là 1 phương thức vô cùng mạnh mẽ và dễ dùng. Nhưng nếu chương trình của bạn không nhận diện ra nó, thì nhớ kiểm tra lại project bạn đã config đúng phiên bản ES2021 chưa nhé ?

// String.prototype.replaceAll(searchValue, replaceValue)

'Hello World'.replace('l', '_');
// → 'He_lo World'

'Hello World'.replace(/l/g, '_');
// → 'He__o Wor_d'

'Hello World'.replaceAll('l', '_');
// → 'He__o Wor_d'

WeakRef và Finalizers

Cần nhấn mạnh rằng JavaScript cũng thực hiện cơ chế “thu gom rác” (garbage collection) giống như Java, C#, Python, tức là tự động thu gom và giải phóng đối tượng hoặc tài nguyên không còn được chương trình sử dụng.

Trong những phiên bản trước đây của JavaScript, hầu hết các tham chiếu được coi là “tham chiếu mạnh” (Strong reference), điều này dẫn đến tình trạng có nhiều đối tượng vẫn tồn tại mặc dù chúng không còn được sử dụng thực sự.

Sự tồn tại của các tham chiếu ảnh hưởng đến quyết định về việc giải phóng tài nguyên trong chương trình. Ngược lại với tham chiếu mạnh, tham chiếu yếu (Weak reference) là dạng tham chiếu không ngăn chặn đối tượng hoặc tài nguyên được tham chiếu khỏi sự thu gom của “bộ gom rác”.

WeakRef

Như đã biết, khi một đối tượng A có tham chiếu tới một đối tượng B, thì đối tượng B sẽ chỉ được “thu gom” khi và chỉ khi đối tượng A đã hoàn toàn bị “thu gom” trước đó. Ta sẽ xem xét qua ví dụ sau:

// Global scope
const theA = {};

(() => {
  // Private scope
  const theB = { foo: 'foo' };
  theA.obj = theB;
})();

Rõ ràng ở đây, đối tượng A nằm ở phạm vi toàn cục, dẫn tới việc đối tượng B trong suốt chương trình sẽ không thể bị “thu gom” do đối tượng A vẫn còn ở đó! ? Hay nói cách khác thì A có một “tham chiếu mạnh” tới B.

Vâng, WeakRef ra đời với mục đích hỗ trợ tạo ra một “tham chiếu yếu” đến một đối tượng. Để nói một cách đơn giản, WeakRef tạo ra một lớp bao bọc giúp truy cập đến đối tượng mà vẫn đảm bảo quá trình “thu gom” diễn ra một cách thuận lợi. Điều này trở nên quan trọng khi chương trình chứa nhiều đối tượng lớn, có thể làm giảm hiệu suất nếu không thu gom chúng đúng cách.

Cách sử dụng WeakRef khá đơn giản, dùng luôn ví dụ trên nhé:

// Global scope
const theA = {};

(() => {
  // Private scope
  const theB = new WeakRef({ foo: 'foo' }); // Use "new" keyword to create a new WeakRef
  theA.obj = theB.defer(); // Use defer to access the object
})();

console.log(theA.obj) // May be "undefined"

Đúng vậy, đầu tiên là khai bảo mới một WeakRef với từ khoá new và đầu vào là đối tượng bạn muốn. Tiếp theo, ta có thể truy cập đối tượng thông qua hàm defer. Nếu đối tượng chưa bị “thu gom”, defer sẽ trả về chính đối tượng đó, còn không thì trả về undefined.

Cần chú ý là việc “thu gom” ở các trình duyệt là có thể khác nhau, vì vậy bạn phải thật cẩn thận khi sử dụng WeakRef.

Finalizers

Đầy đủ hơn là FinalizationRegistry, để cho phép lập trình viên đăng ký các hàm gọi lại (callbacks) được thực thi trong một thời điểm nào đó của việc “thu gom” đối tượng nhất định.

Finalizers được sinh ra cũng một phần là để ta có thể nắm bắt được việc “thu gom” của đối tượng. Nếu tận dụng tốt nó, lập trình viên có thể tìm cách đối mặt được vấn đề “thu gom” khác nhau giữa các trình duyệt mà đã nói ở trên.

Để sử dụng Finalizers vô cùng đơn giản:

console.log('script starting...');

// Create new Finalizers with a callback, may has param "heldValue" or not
const r = new FinalizationRegistry((heldValue) => {
  console.log(heldValue);
});

(() => {
  // Private scope
  const obj = {};
  r.register(obj, "Just got garbage-collected!"); // Register an object to the Finalizers
})();

Với một đoạn code đơn giản như trên, thì khi mà đối tượng obj được “thu gom”, Finalizers sẽ gọi callback và in ra console dòng chữ Just got garbage-collected!

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *