12/08/2018, 14:14

Những tính năng mới nổi bật của ES6 (phần 2)

Trong bài viết trước chúng ta đã cùng điểm qua 5 điểm nổi bật đầu tiên của chuẩn ES6. Hôm nay chúng ta sẽ cùng tiếp tục với phần còn lại. 2.6. Arrow Functions ES6 giới thiệu arrow function với 2 yếu tố chính: ngắn hơn và không bind với this . Hàm ngắn gọn hơn Ví dụ var a = [ ...

Trong bài viết trước chúng ta đã cùng điểm qua 5 điểm nổi bật đầu tiên của chuẩn ES6. Hôm nay chúng ta sẽ cùng tiếp tục với phần còn lại.

2.6. Arrow Functions

ES6 giới thiệu arrow function với 2 yếu tố chính: ngắn hơn và không bind với this.

Hàm ngắn gọn hơn

Ví dụ

var a = [
  "Hydrogen",
  "Helium",
  "Lithium",
  "Beryllium"
];

var a2 = a.map(function(s){ return s.length });

var a3 = a.map( s => s.length );

Với arrow function ta đã loại bỏ keyword "function" và thay vào đó là một fat arrow (=>). Khi hàm chỉ có một param duy nhất ta có thể loại bỏ dấu ngoặc đơn () bao quanh nó. Khi hàm chỉ có một dòng lệnh ta cũng có thể loại bỏ dấu ngoặc nhọn {}.

Không bind với this

Trong Javascript, function có thể coi như một object vì thế mỗi một function sẽ có giá trị this tương ứng của riêng function đó (một object mới nếu function đó là một constructor function, undefined với strict mode, context của object chứa nó khi hàm đó được gọi như một object method).

Xét ví dụ

function Person() {
  this.age = 0;
  setInterval(function grow(){
    this.age++;
  },1000)
}

var p = new Person();

Trong ví dụ trên this trong hàm grow() sẽ trỏ đến object Window thay vì object Person. Để xử lý vấn đề này trong ES5, ta thường dùng "trick" gán giá trị của this cho một biến local và pass giá trị biến đó vào trong hàm.

function Person() {
  var _this = this;
  this.age = 0;
  setInterval(function grow(){
    _this.age++;
  },1000)
}

Arrow function sẽ giúp chúng ta handle vấn đề này tốt hơn. Arrow function không khởi tạo this của riêng nó vì vậy nó sẽ không mutate giá trị this của context bên ngoài nó và tránh được rắc rối khi this trỏ đến object không mong muốn.

function Person() {
  this.age = 0;
  setInterval(() => {this.age++;}, 1000);
}

var p = new Person();

2.7. Promises

Promises là những object chứa thông tin khi event xảy ra hay không xảy ra và nếu event đó có xảy ra thì outcome của nó là gì. Loại object này được tạo ra bên trong hàm asynchronous và được return từ hàm đó. Khi event xảy ra, hàm asynchronous đó sẽ update promise và thông báo sự thay đổi đó với subscriber của nó.

Xét ví dụ sau

setTimeout(function(){
  console.log('Yay!')
  setTimeout(function(){
    console.log('Eh!')
  }, 1000)
}, 1000)

Ví dụ trên gọi hàm callback setTimeout() sau khi event console.log('Yay!') kết thúc. Vấn đề sẽ xảy đến khi ta muốn gọi hàm callback nhiều lần dẫn đến việc hàm bị nested nhiều lần (callback pyramids).

Với promise ta viết lại hàm trên như sau:

var wait1000 = () => new Promise((resolve) => {setTimeout(resolve,1000)});

wait1000().then(function() {
                   console.log('Yay!')
                   return wait1000();
                 })
           .then(function() {
                   console.log('Eh!')
                });

Trong ví dụ trên hàm setTimeout() được pass vào argument là function resolve() và được execute ngay khi new Promise được tạo. Ta có thể thấy hàm setTimeout() luôn chạy ok cho nên hàm resolve() sẽ tự động được gọi để thông báo tới Promise "wait1000" rằng event "1 giây trôi qua" đã xảy ra và "wait1000" sẽ được update để chứa thông tin "resolved" này. Tiếp đó handler function được gọi với wait1000().then(function(){}). Điểm đặc biệt của những handler function này là những gì mà nó return sẽ được coi như một promise mới.

promise.then luôn return một promise

Điều này cho phép ta thực hiện một chuỗi promise và tránh được vấn đề callback pyramids.

2.8. Block-scoped Constructs Let and Const

Với let ta có thể "scope" biến trong block được giới hạn bởi dấu ngoặc nhọn.

Xét ví dụ

function ageFunc(age2) {
  var age = 0;
  if (age2 > 0) {
    var age = age2
  }
  return age;
}

ageFunc(2);

Kết quả là 2 do biến "age" bên trong và bên ngoài if là một. Với let ta có

function ageFunc(age2) {
  let age = 0;
  if (age2 > 0) {
    let age = age2
  }
  return age;
}

ageFunc(2);

Kết quả vẫn là 0 do biến "age" bên trong và bên ngoài if là 2 biến khác nhau.

Với const cũng tương tự

function ageFunc(age2) {
  const age = 0;
  if (age2 > 0) {
    const age = age2;
  }
  return age;
}

ageFunc(2);

Kết quả vẫn là 0 do "age" bên trong và bên ngoài if là 2 constant khác nhau.

2.9. Classes

Nếu bạn đã quen lập trình OOP với những ngôn ngữ khác hẳn bạn sẽ cảm thấy vui mừng với sự xuất hiện của class trong ES6.

Ví dụ class baseModel

class baseModel {
  constructor(options = {}, data = []) {
        this.name = 'Base'
    this.url = 'http://azat.co/api'
        this.data = data
    this.options = options
    }

    getName() {
        console.log(`Class name: ${this.name}`)
    }
}

Để kế thừa từ class trên ta cũng sử dụng những keyword quen thuộc như extends, super

class AccountModel extends baseModel {
  constructor(options, data) {
    super(options, data);
    this.name = "Naruto";
  }

  getName() {
    return this.name;
  }
}

Điểm trừ của class với Javascript là chưa hỗ trợ quản lý việc truy cập dữ liệu bên trong class với những keyword như public, private, protected...

2.10. Modules

Với ES6, module là object được built-in trong Javascript giúp chúng ta export một đoạn code và import để sử dụng trong một file khác. Để thực hiện việc này chúng ta sẽ sử dụng những keyword như export, import.

Xét ví dụ về module với ES6

File moudle.js

export var port = 3000;
export function sayHello() {
  console.log("Hello");
}

File main.js

import {port, sayHello} from 'module';
console.log(port);
sayHello();

Ta cũng có thể import mọi thứ trong module "module.js" bằng việc sử dụng biến

import * as moduleVar from 'module';

console.log(moduleVar.port);
moduleVar.sayHello();

3. Kết luận

Vậy là chúng ta vừa điểm qua những điểm mới nổi bật nhất với sự xuất hiện của ES6. Chuẩn Javascript mới này đã giúp cho việc đọc hiểu, code dễ dàng hơn, giải quyết một số vấn đề còn tồn tại lâu nay của Javascript như scoping, this, native built-in module và khiến Javascript ngày càng trở lên mạnh mẽ hơn.

Tham khảo

http://jamesknelson.com/grokking-es6-promises-the-four-functions-you-need-to-avoid-callback-hell/

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#Lexical_this

https://webapplog.com/es6/

0