Tham chiếu và ghi chú ngắn về ES6, ESNext

1822

ECMAScript

  • Tên chính thức của Javascript
  • Tài liệu chuẩn hóa Javascript
  • Thường được gọi tắt là ES

ES6

ES6 là gì?

  • Bản đặc tả ECMAScript phiên bản 6. Đặc tả tức là bản mô tả chức năng, cú pháp, còn việc có hỗ trợ hay không là phụ thuộc vào phiên bản của từng platform (browser, node.js, babel transpiler,…)
  • Ra đời vào năm 2015 nên còn được gọi là ES2015
  • Là phiên bản nâng cấp lớn của ES5 ra đời vào năm 2009
  • 6 năm cho một lần release là quá lâu và số lượng feature mới cũng là quá nhiều mà chưa có platform nào implement được đầy đủ [1],[2]. Do đó quy trình release đã được thay đổi. Các phiên bản tiếp theo được release theo từng năm và theo quy trình mở [3],[4].
  • Có thể chia ra thành các phiên bản: ES6, ES 2016+ (ES7, ES8, ES9, ES10…) và ES Next (là những chức năng mới sắp được release [5])
  • Vì thường có yêu cầu phải support nhiều phiên bản trình duyệt khác nhau nên chúng ta thường phải sử dụng các transpiler (chẳng hạn babel, swc) để translate ra cú pháp mà support được các browser cũ, do đó khi đọc bảng compatibility table [1] nên chú ý đến cột Babel + core-js

Tham khảo thêm:

letconst

ES6 giới thiệu 2 từ khóa mới là let và const dùng để khai báo biến, trước đây chỉ có var.

Khác với var

Các biến được khai báo với var có phạm vi function scope, còn với letconst thì nó phạm vi block scope

  • function scope: Biến được khai báo dùng được trong toàn bộ function

     
  • block scope: Biến được khai báo chỉ sử dụng được trong block {} nơi mà nó được khai báo

    Hoặc đối với biến trong vòng for:

let vs const

  • let: biến đã khai báo có thể được gán lại
  • const: biến đã khai báo không thể được gán lại

    Tuy nhiên nếu const là object thì giá trị của object vẫn có thể bị thay đổi. Chỉ không thể bị gán thành object khác mà thôi.

Từ đây trở đi chúng ta sẽ chủ yếu dùng let và const.

Template strings

ES6 giới thiệu thêm việc khai báo string bằng ký tự ` (backtick), bổ sung cho ' và ".

Dùng backtick ta có thể khai báo multiline string:

Thay cho trước đây:

Có thể dùng biến hoặc biểu thức JS trong string (String interpolation):

Class

Cú pháp khá giống với các ngôn ngữ class based OOP như PHP hoặc Java.

Class trong ES6 support kế thừa, super (parent) calls, instance method, static methods and constructor.

Constructor

Instance method

Static method

Getter & Setter

ES6 class support define các getter, setter cho các property của object. Cho phép chúng ta custom logic khi đọc/ghi giá trị của property, chẳng hạn validate, biến đổi giá trị…

Nhìn vào class có thể nhầm lẫn name() là một instance method, nhưng ở đây nó là method đặc biệt, chúng ta không thể gọi trực tiếp method này mà từ tên method => tên property.

Inheritance

Class fields

ES vẫn chưa chính thức support việc khai báo private/public property của object class, tính năng này vẫn đang còn được thảo luận (Proposal Stage 3).

Với proposal này ta có thể viết khai báo instance field và static field như sau:

Nếu sử dụng Create React App thì tính năng này cũng đã được enable, chúng ta có thể sử dụng nó:

Enhanced Object Properties

ES6 cung cấp thêm một số cú pháp giúp cho việc khai báo object dễ dàng hơn.

Property Shorthand

Khai báo property và value ngắn gọn:

Tương đương với cách cũ:

Computed Property Names

Property của object có thể là dynamic:

Nếu viết theo cách cũ:

Method Properties

Với cách viết cũ:

Arrow function

Cú pháp viết function ngắn gọn sử dụ dấu => hay còn gọi là fat arrow:

Ví dụ trên tương đương với cách viết cũ như sau:

Tham số trong arrow function

Khi cần truyền tham số:

Nếu chỉ có 1 tham số thì có thể lược bỏ dấu ngoặc đơn:

Expression bodies (Implicit returns)

Không có curly braces => Implicit return

Nếu muốn return object thì sao? => Wrap object bên trong cặp ngoặc ():

Nếu viết theo statement bodies:

Lexical this

Khoan nói về khái niệm lexical this.

this là gì?

Giống như trong các ngôn ngữ lập trình khác như PHP, Java thì this được sử dụng như một đại từ nhân xưng để thay thế cho đối tượng mà chúng ta đang sử dụng.

Ví dụ:

 

this chỉ tồn tại và được sử dụng bên trong object (hay còn gọi là: instance của một class). Thật đơn giản và dễ hiểu!!!

Nhưng ở đây Javascript không như vậy. Điều khác biệt là:

  • Function trong JS cũng là một object và do đó nó có this
  • this được xác định vào lúc function được gọi, dựa theo ngữ cảnh: ai (đối tượng nào) gọi function đó và gọi theo cách nào, chứ không phải dựa theo nơi mà nó được sử dụng (bên trong method của class)

Vậy trong JS có những cách gọi function nào? Ví dụ ta có function sau:

  • Global function call:

    => this được xác định là global object, tương đương với đối tượng window trên trình duyệt hoặc global trong NodeJS.

  • Method call:

     

    => this ở đây là đối tượng obj, do method whoami được obj gọi

  • Thông qua apply() hoặc call(): Nhớ lại, trong JS function cũng là một object và object này có một số method mặc định. Trong đó có apply và call, mục đích của 2 method này giống nhau, chỉ khác trong cách truyền tham số:

    Và mục đích ở đây là: set giá trị của this bên trong function, truyền tham số (nếu có) và gọi function. Ví dụ:

    => this ở đây là giá trị được chỉ định tường minh (explicit binding).

    Ví dụ sử dụng khi function có nhiều tham số:

    Ngoài ra khi làm việc với React, chúng ta có thể hay dùng đến method bind(). Mục đích của method này đó là: copy function tạo ra function mới và set giá trị của this bên trong function mới tạo.

  • Callback, event handler: Ở đây, chúng ta không trực tiếp gọi function mà là khai báo function và đưa cho đối tượng khác sử dụng.

    Chú ý là khi viết pikachu.showDetail tức là chúng ta đang coi nó như là một object, chứ chưa gọi method pikachu.showDetail().

    pikachu.showDetail ở đây được gọi là callback cho hàm setTimeout, tức là khi cần (ví dụ, sau khi đếm ngược hết 5 giây) thì setTimeout sẽ gọi hàm pikachu.showDetail (Hiểu từ theo cách tự nhiên: anh đưa cho em số điện thoại này khi nào cần em gọi lại (callback) cho anh nhé!!! 😄).

    Thế nhưng đối tượng gọi hàm showDetail ở đây không phải là pikachu mà là function setTimeout. Vậy chắc bạn cũng đoán được kết quả rồi.

    Một ví dụ khác:

    Ở đây chúng ta truyền callback cho method forEach là một anonymous function (hàm không tên).

    Ví dụ tương tự với event handler:

    Giống như trong React chúng ta hay viết:

    Tương tự với ví dụ setTimeout, khi có sự kiện click thì đối tượng gọi hàm showDetail ở đây sẽ là button và đến đây thì ai cũng biết kết quả là gì rồi.

    Để có kết quả như ý muốn, ta có thể sửa lại như sau:

     

     

    Xem thêm ví dụ về React => https://codesandbox.io/s/hardcore-johnson-h6eki

lexical this là gì?

Khác với function thông thường, arrow function không tự bind this và chúng ta cũng không dùng được các method call()apply()bind() để “set this” cho function.

this trong arrow function được xác định dựa vào nơi mà nó được define (hoặc là global hoặc bên trong class hoặc bên trong function khác).

Thử thay function thông thường bằng arrow function trong các ví dụ trên:

call()apply()bind() đều không áp dụng được cho arrow function. Ở ví dụ trên, function whoami được define global nên this bên trong function này sẽ được gắn với global object (window).

Cần phải chú ý khi dùng arrow function cho callback, event handler, object method:

Tuy vậy, arrow function là khá hữu ích khi dùng làm callback trong trường hợp này:

Hoặc event handler trong React class component:

Vì arrow function được khai báo bên trong class, this tham chiếu đến chính đối tượng của class này, chứ không tự động bind theo ngữ cảnh lúc gọi nữa.

Hoặc sử dụng proposal class fields (đã được include trong Create React App):

Default parameters

Nếu viết theo cách cũ:

Rest Parameter

Trong variadic function (function có số lượng parameters không cố định) thì tập hợp các tham số còn lại của function có thể được nhóm vào một mảng duy nhất gọi là rest parameter:

Spread Operator

Spread operator ... dùng để extract các phần tử của array (hoặc string) thành các tham số khi gọi function hoặc khi thêm vào array khác.

Function call

Array

  • Tạo array mới chứa array cũ

    Khi chưa có spread:
  • Copy array
  • Concat array
  • Unshift array

Destructuring

Extract values của các phần tử trong mảng, property của object thành các biến.

Array destructuring

Gán giá trị mặc cho biến nếu vượt quá độ dài của array:

Swap giá trị của 2 biến:

Bỏ qua phần tử nào đó trong array:

Rest elements:

Object destructuring

Thay đổi tên biến:

Giá trị mặc định:

Kết hợp việc thay đổi tên biến và giá trị mặc định:

Function parameter destructuring:

Modules

Trong ES6, bạn có thể import và export các chức năng từ các module giữa các file. Những chức năng này có thể là function, class, constant,… về cơ bản là mọi thứ bạn có thể gán vào một biến.

Những module có thể là những file riêng biệt hoặc toàn bộ folder với một file index như là một entry point.

Các câu lệnh import và export giúp bạn thực hiện code splitting, bạn viết code ra nhiều file, giúp cho việc sử dụng lại và maintain, thiết kế các module chức năng. Hơn nữa, nó giúp bạn suy nghĩ về tính đóng gói. Không phải mọi chức năng trong module cần export ra bên ngoài mà chỉ được dùng trong cùng file. Những chức năng được export giống như là public API của module, chỉ những chức năng được export có thể được sử dụng lại bên ngoài.

Có hai cách export:

  • Export theo tên: named export
  • Export default

Named export

VD: Export biến từ file1.js the kiểu named export:

Hoặc export từng biến:

Và sau đó import vào file2.js:

Bạn cũng có thể import tất cả biến được export từ file khác như là một object.

file2.js:

Import có thể có một alias, bạn có thể sử dụng nó trong trường hợp import 2 chức năng từ 2 module có cùng tên : file2.js:

Export default

Với default export, bạn có thể sử dụng trong các trường hợp:

  • Export duy nhất một chức năng
  • Để làm nổi bật chức năng chính của module

Ví dụ file1.js:

Khi import, bạn có thể bỏ qua dấu ngoặc cho việc import default export.

file2.js:

Hơn nữa, tên import có thể khác với export default name.

Bạn cũng có thể kết hợp cả hai kiểu export:

file1.js:

file2.js:

MapSet

Kiểu dữ liệu Map

Tương tự như object dạng key-value, nhưng có những điểm khác biệt:

  • Key của object chỉ có thể là string, number hoặc Symbol, trong khi đó key của map có thể là bất cứ giá trị nào bao gồm cả function, object.
  • Thứ tự insert của key trong Map được giữ nguyên khi lặp
  • Map có method size() để biết số lượng phần tử trong Map
  • Map là một iterable object nên có thể sử dụng trong for..offorEach
  • Object có prototype nên nó sẽ có một số key mặc định
  • Map phù hợp hơn trong các kịch bản thêm, xóa key-value thường xuyên

Kiểu dữ liệu Set

Là một tập hợp, gồm các phần tử có unique value.

for..of

Thêm 1 cú pháp để lặp các iterable objects: String, Array, Map, Set…

Generators

  • Là những function có thể tạm dừng và tiếp tục ở một thời điểm khác sau khi gọi
  • Sử dụng từ khóa function* để khai báo một generator function
  • Sử dụng yield để tạm dừng function và trả về giá trị cho method next()
  • Giá trị trả về khi gọi một generator function đó là một Generator Object
    • Generator Object có thể dùng trong vòng lặp for..of

      Chú ý là for..of ignore giá trị return 6

    • Hoặc gọi nhiều lần method next() resume function, chạy đến từng điểm đã yield trong function cho đến khi gặp return hoặc yield cuối cùng
    • Method next() trả về object có dạng {value: value, done: true/false}value là do yield hoặc return trả về và done để xác định generator đã kết thúc chưa

Promise

  • Hỗ trợ lập trình bất đồng bộ (asynchronous)
  • Promise là một object đại diện cho kết quả hoặc lỗi của một hoạt động bất đồng bộ
  • Thay vì truyền callback vào function, Promise object cho phép chúng ta attach callback và chaining: Ví dụ truyền callback:

    Sử dụng promise:
  • Then chaining Method then() trả về một promise mới, vì thế có thể chaining then() nhiều lần.

    Cần chú ý return value ở then() thì then() sau mới có result Promise chaining dừng nếu có exception, khi đó các then() ở sau bị bỏ qua và chỉ có catch handlers được gọi

    Nếu truyền callback cũ thì có dẫn đến callback hell như thế này:

    Ví dụ với fetch:

    => then() thứ nhất json decode response string, then() thứ hai lấy data từ promise của then() thứ nhất.

  • Chạy song song nhiều async function

  • Sai lầm thường gặp
    • Không return value

    • Nesting promise. Vì then() return promise, không cần phải chaing bên trong callback

    • Không catch error có thể dẫn đến lỗi “Uncaught promise rejections” (có thể làm crash Nodejs app)

ES 2016

Exponent (**) operator

Có thể dùng thay thế cho Math.pow:

Array method includes

Check phần tử có thuộc array:

ES 2017

Object.valuesObject.entries

Object.values trả về mảng giá trị của từng key trong object:

Object.entries trả về mảng cặp giá trị [key, value] của object:

Async / Await

  • Sử dụng Promise như cách lập trình đồng bộ thông thường (synchronous)
  • await chỉ có thể được dùng trong async function để tạm dừng async function chờ kết quả từ một Promise
  • Giá trị trả về function async function luôn là một Promise, vì được ngầm định wrapped trong Promise.resolve()
  • Await trong loop

ES 2018

Object rest/spread operators

  • Support rest/spread operator cho object:
  • Rest properties:
  • Spread properties:

    Clone, merge object:

Promise finally

  • Define callback được thực thi trong cả 2 trường hợp Promise chạy thành công hoặc có lỗi
  • Tránh lặp code trong then() và catch(), chẳng hạn set trạng thái loading khi gọi ajax:

CHIA SẺ