Cuộc đời của EcmaScript

Cuộc đời của ES6 tới ES11

JavaScript là một ngôn ngữ vô cùng hoàn hảo, có thể tạo mọi thứ từ ứng dụng web, ứng dụng dành cho thiết bị di động và ứng dụng phía máy chủ, thậm chí cả ứng dụng máy tính để bàn bằng Electron.js. Khi học JavaScipt bạn không thể không nhắc tới thuật ngữ ES (Viết tắt từ EcmaScript).

Tôi được biết tới bản ES6 khi mới bắt đầu học JavaScript và tôi đã cố gắng tìm hiểu các phiên bản ES trước của nó và cách thức nó hoạt động thế nào, có những thay đổi của mỗi phiên bản ra sao? Cùng tôi tóm lược lại những chi tiết mà tôi thâu tóm lại được nhé 😂

ES1 - Năm 1997

Đây là phiên bản đầu tiên ra lò.

ES2 - Năm 1998

Ra sau ES1 1 năm và chẳng có điều gì thay đổi ở phiên bản này.

ES3 - Năm 1999

Cũng chỉ sau ES2 1 năm nhưng có nhiều sự thay đổi, cú pháp của chúng vẫn còn tồn tại cho tới ngày nay.

Toán tử nghiêm ngặt

Ngoài toán tử == trước đó thì ở phiên bản này toán tử === được sinh ra. Thay vì toán tử == so sánh khác nhau về giá trịkiểu dữ liệu thì toán tử === chúng so sánh cả giá trịkiểu dữ liệu.


const compareTypeAndValue = (num, str) => {
 return num === str;
}
 
console.log(compareTypeAndValue(8, "8")); // False
console.log(compareTypeAndValue(8, 8)); // True

Biểu thức chính quy

Chúng được biểu hiện giữa hai dấu gạch chéo / ... /. Biểu thức nằm ở giữa giấu / ... / và toàn cục, bạn có thể xem cách tôi chọn bỏ qua chữ hoa hoặc chữ thường trong biểu thức và kết thúc nó sau dấu /.


/[^abc]/gim

Cấu tạo biểu thức chính quy

Chúng được xây dựng dưới dạng đối tượng RegExp. Tham số điều tiên dưới dạng biểu thức chính quy, tham số thứ hai là thông số cấu hình cho biểu thức biểu diễn dưới dạng g, i, m.


const regex = new RegExp("[^abc]", "gim");

Switch case

Chúng giống như một bộ điều khiển cơ bản dưới dạng if else. Câu lệnh Switch sử dụng một tham số và so sánh tham số đó với từng Case. Nếu đúng chúng sẽ trả về kịch bản tương ứng với Case đó.


const fizzBuzz = (num) => {
    switch(num) {
     case 1:
       console.log(num);
       break;
     case 2:
       console.log(num);
       break;
     case 3:
       console.log("fizz");
       break;
     case 4:
       console.log(num);
       break;
     case 5:
       console.log("buzz");
       break;
     case 6:
       console.log("fizz");
       break;
     case 7:
       console.log(num);
       break;
     case 8:
       console.log(num);
       break;
     case 9:
       console.log("fizz");
       break;
     case 10:
       console.log(num);
       break;
   }
}
console.log(fizzBuzz(3))

Trình xử lý Try/Catch

Chúng sẽ thông báo lỗi nếu Try không thành công. Dưới đây tôi đã thử cho một obj không xác định, hãy xem trình xử lý Try/Catch sẽ như nào.


const isValidKey = (val1) => {
try {
  let obj;
 
  return obj.hasOwnProperty(val1);
 
} catch (err) {
    throw new Error("not valid key");
}
}
console.log(isValidKey(""))

ES4

Theo mình được biết ES4 không được ra mắt vì TCN39 không thống nhất về kỹ thuật.

Sau ES3 - Năm 1999 thì tới tận Năm 2009 phiên bản ES5 mới trào đời.

ES5 - Năm 2009

Sau 10 năm kể từ phiên bản ES3 thì ES5 có một sự thay đổi kỹ thuật lớn nhất kể từ khi thành lập.

"use strict"

Ở các phiên bản trước, khi biến chưa được khai báo (những biến không sử dụng từ khóa var khi được gọi ban đầu) được phép sử dụng. Cho tới khi sử dụng "use strict", các lỗi sẽ xuất hiện trên màn hình.


"use strict"
 
x = 5; // ReferenceError: x is not defined

Array Methods

every() Kiểm tra phần tử trong mảng có thỏa mãn điều kiện hay không.


var arr = [6, 4, 5, 6, 7, 7];
 
arr.every(function(element) {
 return element % 2 === 0; // Kiểm tra xem có chia hết không
}); // False

filter() Hãy xem nó như là một vòng lặp for có câu lệnh if, nếu một phần tử thỏa mãn điều kiện sẽ được đẩy sang một mảng mới.


var arr = [6, 4, 5, 6, 7, 7];
 
arr.filter(function(element) {
 return element/2 > 3;
})

forEach() Chúng giống như một vòng lặp for, đối với một phần tử được tìm thấy trong một mảng, phương thức forEach() sẽ thực thi và gọi hàm trên đó.


var arr = [6, 4, 5, 6, 7, 7];
 
arr.forEach(function(element) {
 console.log(element * 2);
})

indexOf() & lastIndexOf() indexOf() sẽ tìm kiếm một phần tử cụ thể trong một mảng, chúng trả về tham số đầu tiên khi được tìm thấy, ngược lại chúng trả về -1. Ngược lại với lastIndexOf() chúng trả về tham số cuối cùng khi được tìm thấy, nếu không chúng trả về -1.


var arr = [6, 4, 5, 6, 7, 7];
 
console.log(arr.indexOf(4)); // 1
console.log(arr.indexOf(2)); // -1
console.log(arr.indexOf(7)); // 4
 
console.log(arr.lastIndexOf(7)); // 5

isArray() Đây là một kiểu Boolean, chúng kiểm tra xem có phải là một mảng hay không.


var arr = [6, 4, 5, 6, 7, 7];
 
var str = "Welcome to Lamsaodecode?";
 
console.log(Array.isArray(arr));
console.log(Array.isArray(str));

map() Giống với forEach(), map() lại trả về một mảng hoàn toàn mới.


var arr = [6, 4, 5, 6, 7, 7];
 
arr.map(function(element) {
 return element * 2;
})

reduce() & reduceRight() Phương thức rút gọn này áp dụng một hàm gọi lại cho mỗi phần tử trong mảng. Điều đặc biệt của phương thức Reduce()ReduceRight() là nó giảm mảng thành một phần tử duy nhất.

Phương thức ReduceRight() giống như Reduce() ngoại trừ việc nó lặp từ phải sang trái thay vì từ trái sang phải.


var arr = [6, 4, 5, 6, 7, 7];
 
var reduced = arr.reduce(function(curr, next) {
 return curr + next;
}, 0);
 
var reducedRight = arr.reduceRight(function(curr, next) {
 return curr + next;
}, 0)
 
console.log(reduced);
 
console.log(reducedRight);

some() Gần giống với every() ngoại trừ phương thức kiểm tra xem liệu có ít nhất một phần tử thỏa mãn điều kiện bạn đã đặt cho nó hay không.


var arr = [6, 4, 5, 6, 7, 7];
 
arr.some(function(element) {
 return element % 2 === 0; // Kiểm tra điều kiện
}); // True

JSON

Khả năng phân tích cú pháp và chuỗi của JavaScript Object Notation (JSON) đã trở thành một khả năng trong tiêu chuẩn ES5. Định dạng JSON về cơ bản được sử dụng để truyền một số loại dữ liệu có cấu trúc qua kết nối mạng, thường là một ứng dụng web và một API.

Khi chuyền dữ liệu từ một chuỗi, sử dụng JSON.stringify() để chuyển đổi các đối tượng thành một chuỗi. Sau đó, sử dụng JSON.parse() để chuyển đổi lại dữ liệu sau khi truyền trở lại đối tượng của JavaScript.


var arr = [6, 4, 5, 6, 7, 7];
var obj = {
 author: "Jos Hoàng Tiên",
 title: "Làm sao để code?",
 published: false
}
console.log("======== Ví dụ ARR ==========");
console.log("orig arr=====>", arr);
console.log("stringified arr=====>", JSON.stringify(arr));
console.log("proof of type=====>", typeof JSON.stringify(arr));
console.log("parsed string=====>", JSON.parse(JSON.stringify(arr)));
console.log("proof of type=====>", typeof JSON.parse(JSON.stringify(arr)), "\n\n");
 
console.log("======== OBJ EXAMPLE ==========");
console.log("orig obj=====>", obj);
console.log("stringified obj=====>", JSON.stringify(obj));
console.log("proof of type=====>", typeof JSON.stringify(obj));
console.log("parsed string=====>", JSON.parse(JSON.stringify(obj)));
console.log("proof of type=====>", typeof JSON.parse(JSON.stringify(obj)), "\n\n");

Date Methods

Có hai phương thức đối tượng Date mới đã được giới thiệu trong ES5 tương đương về mặt chức năng. Date.now()new Date().ValueOf() đều trả về thời gian hiện tại tính bằng mili giây kể từ ngày 1 tháng 1 năm 1970.


console.log(Date.now());
 
console.log(new Date().valueOf());

Sự khác biệt lớn nhất giữa hai phương thức valueOf() là một phương thức trên một thể hiện của đối tượng DateDate.now() là một hàm tĩnh của đối tượng Date.

getters và setters

Trong ES5, chúng ta đã được giới thiệu về các accessor properties. Đây là những hàm có mục đích duy nhất là lấy hoặc đặt một giá trị, trông giống như các thuộc tính tiêu chuẩn khi gọi chúng.


let character = {
 first_name: "Jos",
 last_name: "Tiên",
 
 get fullName() {
   return `${this.first_name} ${this.last_name}`;
 },
 set fullName(str) {
   [this.first_name, this.last_name] = str.split(" ");
 }
 
};
 
console.log(character.fullName); // Jos Tiên
 
character.fullName = "Zunokie"
 
console.log(character.first_name);
console.log(character.last_name);

ES6 - Năm 2015

Sau 7 năm trôi qua phiên bản ES6 được ra thay thế và hoàn thiện hơn phiên bản ES5 trước đó.

Babel

Một trong những thay đổi lớn nhất từ ES5ES6 không thể được biên dịch trực tiếp trong các trình duyệt. Chúng cần sử dụng một trình chuyển đổi có tên Babel.js để tạo đoạn mã JavaScript tương thích mà các trình duyệt cũ hơn có thể đọc được.

Để sử dụng Babel khi dự án của bạn được xây dựng, bạn cần thêm package.json vào dự án của mình.

Để cấu hình dự án của mình, bạn cần cài đặt NodeNPM (Bạn có thể dùng NodeYarn). Trên Command nhập npm init hoặc lệnh fiber init. Để xem các cấu hình bạn xem tại file package.json.

Sử dụng npm để thêm babel vào dự án


npm install --save-dev babel-cli
// hoặc
yarn add babel-cli --dev

Cuối cùng, trong thư mục gốc của dự án (nơi chứa package.json), hãy tạo tệp .babelrc. Đây là tệp cấu hình yêu cầu Babel chuyển đổi mã của bạn thành ES5.


npm install --save-dev babel-preset-env
// hoặc
yarn add babel-preset-env --dev

Sau đó kiểm tra trong file .babelrc


{
    "presets": ["env"]
}
Nếu bạn sử dụng thư viện hoặc JavaScript Framework như create-react-app, nhiều khả năng Babel đã được định cấu hình cho bạn.

Big Arrow (Fat Arrow) Functions

Trước khi có tiêu chuẩn mới này, JavaScript đã sử dụng từ khóa function để tạo ra các hàm. Bây giờ, chúng ta có thể sử dụng hàm mũi tên, => để viết các hàm.


// Trước ES6
function add(num1, num2) {
 return num1 + num2;
}
 
// ES6 (implicit return)
const addImplicit = (num1, num2) => num1 + num2;
 
console.log(add(3, 4));
console.log(addImplicit(3, 4));

Một lớp con của ES6 có một implicit return. Nếu hàm có nhiều hơn một dòng, chúng sẽ cần dấu ngoặc nhọn và câu lệnh trả về


// ES6 (explicit return)
 
const addExplicitReturn = (num1, num2) => {
 let sum = num1 + num2;
 return sum;
};
 
 
console.log(addExplicitReturn(3, 4));

Cũng cần lưu ý rằng khi bạn đang sử dụng các lớp, hàm mũi tên được liên kết với từ khóa this, do đó thực sự không cần sử dụng phương thức bind() để liên kết hàm với lớp.

Classes

Chúng đóng vai trò như một cú pháp trên các thuộc tính của JavaScript. Thay vì Prototypal Inheritance, chúng dụng Classical Inheritance với expand.


class StarWarsCharacter{
 constructor(attributes) {
   this.name = attributes.name;
   this.age = attributes.age;
   this.homePlanet = attributes.homePlanet;
 }
 
 getCharacter = () => `Tôi là ${this.name}, tôi ${this.age} tuổi và tôi đến từ ${this.homePlanet}.`;
}
 
const tien = new StarWarsCharacter({ name: "Jos Hoàng Tiên", age: 21, homePlanet: "Nam Định"});
 
tien.getCharacter();
 
class Heroes extends StarWarsCharacter {
 constructor(attributes) {
   super(attributes);
   this.favoriteInstrument = attributes.favoriteInstrument;
 }
  getFavoriteInstrument = () => `Tôi là ${this.name}, tôi ${this.age} tuổi và nhạc cụ yêu thích của tôi là ${this.favoriteInstrument}`;  
}
 
const hiep = new Heroes({ name: "Hiệp MinMin", age: 21, favoriteInstrument: "Piano"});
 
console.log(hiep.getFavoriteInstrument());

Destructuring

Cấu trúc của một Object rất tuyệt vời để giảm sự lộn xộn của đoạn mã giúp chúng dễ nhìn hơn. Chúng cho phép chúng ta giải nén một đối tượng và sử dụng giá trị đã giải nén đó làm biến mà chúng ta sử dụng đến sau này trong đoạn mã.


const state = {
 name: "Jos Hoàng Tiên",
 age: 21,
 dark_side: false
}
 
console.log("Trước destructuring");
 
console.log(state.name); // Đối tượng có tiền tố trước đó
console.log(state.age);  // Hủy bỏ destructuring
console.log(state.dark_side);
 
 
const { name, age, dark_side } = state;
 
console.log("Sau khi destructuring");
console.log(name); // Có thể truy cập và sử dụng lại!
console.log(age); 
console.log(dark_side);

Trong phần Trước destructuring, chúng ta phải sử dụng tên đối tượng ngoài thuộc tính mà chúng ta muốn truy cập thuộc tính đó. Chúng ta có thể hủy cấu trúc của nó bằng cách kéo các thuộc tính ra, đặt nó vào một tập hợp các dấu ngoặc nhọn và đặt nó thành tên đối tượng.

Đảm bảo sử dụng từ khóa const trước dấu ngoặc nhọn. Chúng cho phép truy cập các thuộc tính này dưới dạng các biến thay vì sử dụng ký hiệu dấu chấm trên chính đối tượng của nó.


const arr_state = [ "Jos Hoàng Tiên", 21, false];
 
console.log("Trước destructuring");
 
console.log(arr_state[0]); // Đối tượng có tiền tố trước đó
console.log(arr_state[1]);  // Hủy bỏ destructuring
console.log(arr_state[2]);
 
console.log("\n\n\n")
const [ name, age, dark_side ] = arr_state; // Gán một biến cho mỗi biến trong mảng
 
console.log("after destructuring");
console.log(name); // Có thể truy cập và sử dụng lại!
console.log(age); 
console.log(dark_side);

let và const

Trong ES6, có một số biến mới về cơ bản đã thay thế từ khóa var. Trước ES6, biến chỉ có phạm vi toàn cục (Global Scope). Với việc bổ sung letconst, bây giờ biến có phạm vi cục bộ (Block Scope).


let x = 5;
 
function blockExample() {
 let x = 2 // Phạm vi của biến function scope;
 if(x >= 3) {
   let x = 10; // Phạm vi của block scope
   console.log(x, "Bên trong khối");''
 } else {
   let x = 1;
   console.log(x, "Bên trong một khối khác")
 }
 console.log(x, "Bên trong một hàm");
}
 
blockExample();
console.log(x, "Phạm vi toàn cục");

Từ khóa let có thể được gọi lại nếu cần khi được sử dụng trong cùng một phạm vi, nếu khai báo lại của cùng một biến sẽ gây ra lỗi cú pháp.


// Trước ES6
var x = 5;
var x = 120; // Không xảy ra lỗi

// ES6
let x = 5;
let x = 120; // Lỗi cú pháp

Từ khóa const rất hữu ích khi ta có một biến mà không có ý định gán lại, nếu gán lại biến cho một giá trị khác sẽ gây ra lỗi cú pháp.

Promises

Sử lý trình duyệt JavaScript không đồng bộ một cách mượt mà và tốt hơn.


console.log("Trước promise")
let promise = new Promise((resolve, reject) => {
  let resolvedFlag = false;
  // Gán cờ để kiểm tra tính logic
  console.log("Gọi API");
  resolvedFlag = true; // Chuyển thành True khi chúng được thực hiện trong
 
  if(resolvedFlag) { // Nếu đúng thì sẽ gọi hàm   
  	resolve("Đây là Promise");
  } else { // Nếu sai sẽ trả về lỗi
    reject(new Error("Promise sai"));
    console.log("Sau khi Promise");
  }
});
 
promise.then(resp => {
  console.log(resp); // Promise response
})

Rest và Spread

Toán tửRest và toán tửSpread về cơ bản là cùng một cú pháp, nhưng phục vụ một mục đích khác. Toán tử Rest được sử dụng trước một tham số hàm để chỉ ra nhiều đối số nên được gán cho tham số đó.


function restExample(a, ...b) {
 console.log(a); // 1
 console.log(b); // [2, 3, 4, 5, 6]
}
 
restExample(1, 2, 3, 4, 5, 6);

Toán tử Spread sử dụng bởi các mảng. Về cơ bản, chúng lấy nội dung của mảng, sao chép nó để nó có thể được truyền vào cấu trúc mới. Vậy ta có thể sử dụng toán tử spread để thêm gì đó vào mảng mà không cần phải sử dụng push() hoặc unshift().


function spreadExample(arr) {
 let newArr = [2, 4, 6, 8];
 console.log("arr", arr);
 let combinedArr = [...newArr, ...arr]; // Đẩy các tham số của newArr và arr vào mảng một chiều
 let arrWithOtherContents = ["a", ...newArr, {b: "c", d: "e"}, true, ...arr];
 console.log(arrWithOtherContents);
 console.log("combined", combinedArr);
}
 
console.log(spreadExample([1, 3, 5, 7, 9]))

Template Literals

Trong ES6, chúng ta không cần các toán tử + để nối chuỗi, ở đây chúng ta sử dụng ` ... ` để thao tác một chuỗi bên trong nó.


let name = "Jos Hoàng Tiên";
let holiday = "Christmas";
 
// Trước ES6
console.log("Tên tôi là" + name + ", tôi thích ngày nghỉ lễ " + holiday);
 
// Trong ES6
console.log(`Tên tôi là ${name}, tôi thích ngày nghỉ lễ ${holiday}`);

ES7 - Năm 2016

Array.prototype.includes


// Trước ES7
const numbers = [4, 8, 15, 16, 23, 42];
if (numbers.indexOf(42) !== -1) {
  // ...
}

// ES7
const numbers = [4, 8, 15, 16, 23, 42];
if (numbers.includes(42)) {
  // ...
}

Exponentiation Operator


// Trước ES7
const old = Math.pow(3, 7);
// 2187

// ES7
const es7 = 3 ** 7;
// 2187

ES8 - Năm 2017

Object.values() và Object.entries()

Object.entries()


const obj = { 0: 'Làm', 1: 'sao', 2: 'để', 3: 'code?' };
console.log(Object.entries(obj)[1]);

// Kết quả
Array ["1", "sao"]

Object.values()


var check = ['x', 'y', 'z'];
console.log(Object.values(check));

// Kết quả
Array ["x", "y", "z"]

String.prototype.padEnd() và String.prototype.padStart()

String.prototype.padStart()


const str1 = '5';

console.log(str1.padStart(2, '0'));
// expected output: "05"

const fullNumber = '2034399002125581';
const last4Digits = fullNumber.slice(-4);
const maskedNumber = last4Digits.padStart(fullNumber.length, '*');

console.log(maskedNumber);
// Kết quả: "************5581"

String.prototype.padEnd()


const str1 = 'Làm sao để code?';

console.log(str1.padEnd(25, '.'));
// expected output: "Làm sao để code?........"

const str2 = '200';

console.log(str2.padEnd(5));
// Kết quả: "200"

Async function (async/await)


function resolveAfter2Seconds() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('resolved');
    }, 2000);
  });
}

async function asyncCall() {
  console.log('calling');
  var result = await resolveAfter2Seconds();
  console.log(result);
  // Kết quả: 'resolved'
}

asyncCall();

ES9 - Năm 2018

Asynchronous iteration


for await (let book of books) {
     console.log(book)
};

Rest operator


const fruits = { orange: 1, apple: 10, banana: 4, }
const { orange, ...rest } = fruits;
console.log(rest); // { apple: 10, banana: 4 };

// Trong hàm
function getFruits(apple, ...rest) {
     return rest.banana;
}

Promise.prototype.finally


let isLoading = true;

fetch(myRequest).then(function(response) {
    var contentType = response.headers.get("content-type");
    if(contentType && contentType.includes("application/json")) {
      return response.json();
    }
    throw new TypeError("Lỗi không có JSON!");
})
.then(function(json) { /* Xử lý JSON */ })
.catch(function(error) { console.log(error); })
.finally(function() { isLoading = false; });

ES10 - Năm 2019

Optional Catch Binding


// Trước ES10
try {
  doSomethingThatMightThrow();
} catch (exception) {
  // Đặt tên để ràng buộc, ngay cả khi không sử dụng nó!
  handleException();
}

// ES10
try {
  doSomethingThatMightThrow();
} catch { // → Sử dụng catch mà không cần cả tham số!
  handleException();
}

Object.fromEntries()


const entries = new Map([
  ['foo', 'bar'],
  ['baz', 42]
]);

const obj = Object.fromEntries(entries);

console.log(obj);
// expected output: Object { foo: "bar", baz: 42 }

Array.flat()


var arr1 = [1, 2, [3, 4]];
arr1.flat(); 
// [1, 2, 3, 4]

var arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]

var arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]

var arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
arr4.flat(Infinity);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Array.flatMap()


let arr1 = ["Làm sao để code?", "", "Jos Hoàng Tiên"];

arr1.map(x => x.split(" "));
// [["Làm","sao","để","code?"],[""],["Jos Hoàng Tiên"]]

arr1.flatMap(x => x.split(" "));
// ["Làm","sao","để","code?", "", "Jos Hoàng Tiên"]

String.trimStart() và String.trimEnd()

String.trimStart()


var greeting = '   Làm sao để code?   ';

console.log(greeting);
// expected output: "   Làm sao để code?   ";

console.log(greeting.trimStart());
// expected output: "Làm sao để code?   ";

String.trimEnd()


var greeting = '   Làm sao để code?   ';

console.log(greeting);
// expected output: "   Làm sao để code?   ";

console.log(greeting.trimEnd());
// expected output: "   Làm sao để code?";

Dynamic Import


// Default export
export default () => {
  console.log('Export default!');
};

// Export `doStuff`
export const doStuff = () => {
  console.log('Chờ…');
};

globalThis Object


const global = Function('return this')();

const getGlobal = function () {
  if (typeof self !== 'undefined') { return self; }
  if (typeof window !== 'undefined') { return window; }
  if (typeof global !== 'undefined') { return global; }
  throw new Error('unable to locate global object');
}

const global = getGlobal(); // trả về window object trên trình duyệt

// Ví dụ về mảng
const numbers = new global.Array(1, 2, 3);
console.log(numbers); // outputs [1, 2, 3];

ES11 - Năm 2020

Optional Chaining ?.


const player = {
    details: {
        name: {
            firstName: "Hiệp",
            lastName: "MinMin",
	   		age: 20
        }
    },
    jobs: [
        "Piano",
        "Guitar"
    ]
}

const playerFirstName = player?.details?.name?.firstName;

Private fields #


class Foo {
  #b = 15;
  a = 10;
  get() {
    return this.#b;
  }

  increment() {
    ++this.#b;
  }
}
const obj = new Foo();

obj['#b']; // undefined
obj.a = 10;
obj.hasOwnProperty('#b'); // False

undefined javascript - Nullish Coalescing ??


const blogger = input.blogger || 'Làm sao để code?';

Nhưng nếu input.player sẽ là một trong những falsy values thì như thế nào, do đó sử dụng tính năng mới này Nullish Coalescing ??


const blogger = input.blogger ?? 'Làm sao để code?';

ES12 - Năm 2021

Những cải tiến đáng quan trọng trong năm 2021 này

replaceAll Chúng thay thế tất cả các lần xuất hiện của một chuỗi bằng một giá trị chuỗi khác. Ban đầu, trước kia Replace chỉ thay thế lần xuất hiện đầu tiên của một chuỗi bằng một số giá trị khác trong khi ReplaceAll có thể được sử dụng để thay thế tất cả các trường hợp.


let string = "Làm sao để code có phải là một trang chỉ để code?"

// Trước kia replace
console.log(string.replace("code", "hack"));
// Kết quả: Làm sao để hack có phải là một trang chỉ để code?

// Sau đó replaceAll
console.log(string.replaceAll("code", "hack"));
// Kết quả: Làm sao để hack có phải là một trang chỉ để hack?

Numeric separator Xử lý với số lượng lớn có thể tạo ra một số nhầm lẫn trong khi đọc mã. Ví dụ nếu viết 9000000000, có lẽ sẽ không ai đọc hay biết giá trị của con số này. Vì vậy, phiên bản ES12 này đã giới thiệu một dấu phân tách số, vậy con số trên có thể được viết là 9_000_000_000 hay cách nói đơn giản là đọc giá trị của nó là 9 tỷ.

Có thể kiểm tra xem cú pháp có hợp lệ hay không?


const number = 9000000000;
console.log(number === 10 ** 9);
// Kết quả: true

Private Methods Tính năng này giúp tạo các phương thức trong một lớp mà chỉ có thể truy cập vào các phương thức mà chúng được định nghĩa.


class PrivateMethods {
  // Phương thức Private
  #privateName() {
  	console.log("Có sẵn trong phương thức Private");
  }

  // Phương thức Public
  publicName() {
    this.#privateName();
    console.log("Có sẵn trong tất cả đối tượng của Private");
  }
}

const method = new PrivateMethods();
method.publicName();
// Kết quả: Có sẵn trong phương thức Private
// Có sẵn trong tất cả đối tượng của Private

method.privateName();
// Error: hàm method.privateName không xác định

Promise.any() Nó gần như đối lập với Promise.all - được thực thi khi tất cả các lời hứa được thực hiện trong khi điều kiện được giải quyết trong khi Promise.any được thực thi nếu khi bất kỳ điều kiện nào đó được thực hiện.


const first = new Promise((resolve, reject) => {
  reject("Không thực hiện");
});

const second = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, "Sẽ thực hiện trong vòng 500s");
});

const third = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, "Sẽ thực hiện trong vòng 100s");
});

Promise.any([first, second, third]).then((response) => {
  console.log(response);
})

// Kết quả: Sẽ thực hiện trong vòng 100ms

Ở trên, có thể thấy rằng const third được thực hiện đầu tiên vì nó sẽ được thực hiện sau 100 mili giây trong khi const second sẽ thực hiện sau 500 mili giây trong const first không thực hiện. Nhưng điều gì sẽ xảy ra nếu tất cả không thực hiện. Ở đây, ta có thể sử dụng try catch để bắt nếu tất cả khi không thực hiện hoặc thực hiện sau đó và chuỗi catch để bắt lỗi.


const first= new Promise((resolve, reject) => {
  reject("Không thực hiện");
});

const second= new Promise((resolve, reject) => {
  setTimeout(reject, 500, "Sẽ thực hiện trong vòng 500s");
});

const third= new Promise((resolve, reject) => {
  setTimeout(resolve, 100, "Sẽ thực hiện trong vòng 100s");
});

Promise.any([first, second, third]).then((response) => {
  console.log(response);}).catch(err => console.log(err))

// Kết quả: Aggregate Error: No Promise in Promise.any was resolved
Tác giả: Jos Hoàng Tiên
Hãy mua cho mình một cuốn notebook và một cây bút kể cả khi bạn là dân coder.