What's new in ES8

ES8 (tên chính thức là ES2017) vừa mới được chính thức release cuối tháng trước. Nếu bạn còn nhớ thì ES5 được release từ tận năm 2009. Vậy mà chỉ trong 3 năm qua, spec của ECMAScript đã được update đến 3 lần, và năm sau có lẽ cũng sẽ không khác. Như thế đã đủ để thấy sự trỗi dậy thần kì của JavaScript trong những năm gần đây. Hãy cùng tìm hiểu xem chúng ta có gì mới trong năm nay nhé. Nếu bạn đang dùng Google Chrome (version 59) thì bạn có thể mở DevTool ra và thử ngay những tính năng mới này.

String padding

prototype của String có thêm 2 function mới String.prototype.padStartString.prototype.padEnd. Syntax của 2 function mới này như sau

str.padStart(targetLength [, padString])
str.padEnd(targetLength [, padString])

2 function này dùng để pad phần đầu hoặc cuối một String với kí tự trong argument padString đến độ dài targetLength. Giá trị mặc định của padString là khoảng trắng.

Ví dụ

'abc'.padStart(6);          // '  abc'
'abc'.padStart(6, '-');     // '--abc'
'abc'.padEnd(6);            // 'abc  '
'abc'.padEnd(6, '-');       // 'abc--'

Object API

Lần này API của Object có thêm vài function như sau

Object.values

Từ ES6, chúng ta đã có Object.keys để dễ dàng lấy toàn bộ key của một object. Bây giờ chúng ta sẽ có thêm Object.values để lấy toàn bộ value của một object. Cách dùng cũng tương tự như Object.keys

Object.values(obj)

Ví dụ

Object.values({
    a: 'apple',
    b: 'boy',
    c: 'cat',
    d: 'dog'
}); // ['apple', 'boy', 'cat', 'dog']

Object.entries

Object.entries thì là một sự kết hợp giữa Object.keysObject.values. Nó sẽ return tất cả cặp keyvalue của object. Syntax của nó cũng giống hệt 2 cái kia

Object.entries(obj)

Ví dụ

Object.entries({
    a: 'apple',
    b: 'boy',
    c: 'cat',
    d: 'dog'
}); // [['a', 'apple'], ['b', 'boy'], ['c', 'cat'], ['d', 'dog']]

Object.getOwnPropertyDesriptors

Cái này thì cũng gần giống với Object.getOwnPropertyDescriptor đã có từ cả tỉ năm trước. Nhưng thay vì chỉ trả về miêu tả cho 1 thuộc tính thì Object.getOwnPropertyDesriptors sẽ cho miêu tả tất cả thuộc tính của một object (trừ các thuộc tính thừa kế từ prototype). Các giá trị của miêu tả có thể bao gồm value, writable, get, set, configurable, enumerable. Syntax của nó như sau

Object.getOwnPropertyDesriptors(obj)

Ví dụ

Object.getOwnPropertyDesriptors({a: 1, b: 2});

Kết quả sẽ là

{
    a: {
        configurable: true,
        enumerable: true,
        value: 1,
        writable: true
    },
    b: {
        configurable: true,
        enumerable: true,
        value: 2,
        writable: true
    },
}

Function này sẽ dùng khi bạn muốn thực sự clone một thuộc tính của object, tức là cả gettersetter của nó.

Ví dụ bạn có object

const a = {
    name: 'John Smith',
    set name(value) {
        store.dispatch('NAME_CHANGING', name, value)
        this.name = value
    }
}

Nếu bạn muốn extend object này, dùng Object.assign thì sẽ chỉ có giá trị của thuộc tính name được copy thôi. Nếu bạn muốn copy cả setter của nó thì phải làm thế này

const b = Object.create(
    Object.getPrototypeOf(a),
    Object.getOwnPropertyDescriptors(a)
);

Nếu bạn định viết một Framework mới thì cái này có thể sẽ hữu ích nhỉ.

Function arguments/parameters list trailing comma

Cũng giống như array và object, bây giờ bạn có thể viết dấu phẩy ở cuối phần khai báo tham số cho function hoặc các giá trị truyền vào khi gọi function.

Ví dụ

const s = (a, b,) => a + b; // No SyntaxError anymore
s(1, 2,); // 3. No SyntaxError anymore

Nếu bạn cần truyền một đống giá trị cho một function (như function ga của Google Analytics chẳng hạn) thì cái này có thể sẽ giúp bạn tách các giá trị thành nhiều dòng dễ chịu hơn một chút

someFunction(
    firstArg,
    secondArg,
    thirdArg,
    fourthArg,
);

Async/await

Tính năng này đã được mọi người bàn đến từ rất lâu rồi, có thể bạn cũng đang sử dụng tính năng này trong các project hiện tại rồi. Nếu bạn từng làm việc với C# thì syntax này có lẽ khá quen thuộc với bạn. Với syntax này, bạn có thể viết các đoạn code asynchronous theo kiểu synchronous, nghĩa là không cần Promise hay callback function.

Ví dụ một đoạn code dùng Promise thế này

function getContent = () => getContentFromServer()
    .then(() => {
        this.setContent(content);
        // do other things with content here
    })
    .catch((error) => {
        // handle error
    });

Khi viết bằng async/await thì sẽ thế này

async function getContent() {
    try {
        const content = await getContentFromServer();
        this.setContent(content);
        // do other things with content here

        return content;
    } catch (error) {
        // handle error
    }

}

Thật ra async function cũng sẽ return một Promise. Và cái mà bạn await thật ra cũng là một Promise hoặc một async function khác. Vậy nên tính năng này thật ra là một syntax tiện lợi hơn cho Promise. Vậy nên tốt nhất bạn nên hiểu về Promise trước khi dùng đến syntax này. Nhưng nếu bạn không có vấn đề gì với Promise thì đây là một tính năng rất tuyệt vời.

Tất nhiên vì nó return một Promise nên bạn cũng có thể dùng nó như một Promise

Promise.all([getContent(), someOtherAsyncFunction()]]);

Shared memory and atomics

Tính năng này được thêm vào để các thread (main thread, web worker) có thể cùng truy cập một dữ liệu (read/write).

Một object SharedArrayBuffer sẽ được dùng để biểu diễn dữ liệu này. Syntax để tạo một SharedArrayBuffer như sau

new SharedArrayBuffer(length) // length, in bytes, is the size of the buffer to create

Ví dụ

const buffer = new SharedArrayBuffer[1024]

Để truy cập dữ liệu này, ta dùng đến object Atomics. Object này có các static method để thay đổi dữ liệu trong một SharedArrayBuffer. Nó sẽ đảm bảo các thao tác lên dữ liệu được thực hiện lần lượt, không bị gián đoạn. Bạn có thể xem các static method của Atomicđây. Dưới đây là một ví dụ sử dụng SharedArrayBufferAtomics

// worker.js
onmessage = (event) => {
    doSomething(event.data.sab)
}

const doSomething = (sab) => {
    const ta = new Uint8Array(sab);

    Atomics.add(ta, 0, 12); // returns 0, the old value
    Atomics.load(ta, 0); // 12
}

// main thread
const sab = new SharedArrayBuffer(1024);
const worker = new Worker('worker.js');
worker.postMessage({ sab })

Lifting template literal restriction

Đây là một tính năng đã có mặt trong final proposal nhưng không có mặt trong đợt update này. Có thể nó sẽ được thêm vào trong ES9. Hiện giờ cũng chỉ có mỗi Firefox (53) hỗ trợ tính năng này. Ngắn gọn thì bây giờ bạn đã có thể dùng Tagged template literals. Tag là một function để parse template literal. Tóm lại là một syntax thuận tiện và linh hoạt hơn cho template literal.

Dưới đây là một ví dụ hoàn chỉnh (MDN)

var person = 'Mike';
var age = 28;

function myTag(strings, personExp, ageExp) {

  var str0 = strings[0]; // "that "
  var str1 = strings[1]; // " is a "

  // There is technically a string after
  // the final expression (in our example),
  // but it is empty (""), so disregard.
  // var str2 = strings[2];

  var ageStr;
  if (ageExp > 99){
    ageStr = 'centenarian';
  } else {
    ageStr = 'youngster';
  }

  return str0 + personExp + str1 + ageStr;

}

var output = myTag`that ${ person } is a ${ age }`;

console.log(output);
// that Mike is a youngster

Things that didn't make it

Có một vài thứ khá thú vị có mặt trong proposal nhưng không xuất hiện trong lần update này. Mình rất hy vọng là nó sẽ có mặt trong lần update tới 😃