Những tính năng của ECMAScript 6

ECMAScript là gì

ECMAScript là một đặc tả kỹ thuật của scripting language, được thiết kế bởi tổ chức Ecma International. Có nhiều implementation của đặc tả này, trong đó phổ biến nhất chính là JavaScript (ngoài ra có thể kể đến JScript, Action Script).

Lần đầu tiên ngôn ngữ Javascript xuất hiện là vào năm 1995 bởi Sun Microsystem và Netcapse. Sau đó một năm, trình duyệt Netcapse phát hành phiên bản hỗ trợ khả năng xử lý javascript làm thay đổi đáng kể bộ mặt của web thời đó. Cuối năm 1996, Microsoft cũng phát triển một phiên bản tương tự JavaScript của Netcapse lấy tên là JScript, nó được tích hợp vào phiên bản trình duyệt Internet Explorer 3.0. Để cuộc chiến trình duyệt không làm ảnh hưởng tới những nhà phát triển và người sử dụng, Netcapse đã giao JavaScript cho tổ chức ECMA quốc tế chuyên về chuẩn hóa, và tài liệu đặc tả tiêu chuẩn cho JavaScript ra đời với mã tài liệu ECMA-262.

Những đặc tả này trở thành những chuẩn mà lập trình viên tuân theo để việc viết code gọn gàng sáng sủa hơn. Trong bài viết này mình sẽ sử dụng Javascript, implementation phổ biến nhất của ECMAScript cũng chính là ngôn ngữ được sử dụng rộng rãi khi thực hiện các xử lí client-side khi phát triển web.

ES5 vs ES6

Lịch sử phát triển của ECMAScript

  • 6/1997: Ra mắt phiên bản thứ nhất.
  • 6/1998: Ra mắt phiên bản thứ hai.
  • 12/1999: Ra mắt phiên bản thứ ba. Phiên bản thứ tư sau một thời gian draft cuối cùng đã không được ra mắt.
  • 12/2009: Ra mắt phiên bản thứ năm, ES5.
  • 6/2011: Ra mắt phiên bản 5.1.
  • 6/2015: Ra mắt phiên bản thứ sáu, tên chính thức là ECMAScript 2015, tuy nhiên vẫn hay được gọi dưới cái tên ES6.

Trong lịch sử phát triển của ECMAScript thì ngoại trừ phiên bản 4 chết yểu thì 3 phiên bản đầu được phát triển từ cách đây khá lâu (năm ra mắt 1997, 1998, 1999). Có thể nói phiên bản 5, ES5 (ra mắt năm 2009) là phiên bản được sử dụng rộng rãi nhất và phiên bản mới nhất được ra mắt của ECMAScript là ES6 là những phiên bản đáng quan tâm nhất của ECMAScript. Tiếp theo, chúng ta cùng xem thử ES6 có gì mới so với ES5

Những tính năng mới trên ES6

Constant

ES6 hỗ trợ khai báo constant hay còn được biết dến là "immutable variables" Việc hỗ trợ constant giúp chúng ta viết code ngắn gọn và tiện hơn nhiều so với trên ES5

ES6:

const PI = 3.141593

ES5:

Object.defineProperty(typeof global === "object" ? global : window, "PI", {
  value:        3.141593,
  enumerable:   true,
  writable:     false,
  configurable: false
  })

Block-scope

ES6 cho phép chúng ta sử dụng biến và function trong phạm vi block-scoped Câu lệnh let cho phép chúng ta khai báo biến của block, các biến mà chỉ có hiệu lực trong block đó thôi. Dưới đây là 1 so sánh so với khi sử dụng khai báo var

function foo() {
  {
    console.log(hi); //error, biến `hi` chưa được định nghĩa
    //phần code bên trong dấu ngoặc được gọi là block
    let hi = 1;
  }
  console.log(hi); //error, biến `hi` chưa được định nghĩa

  for(let i = 0; i < 10; i++) {
    //biến `i` chỉ có hiệu lực trong block này
  }àyày

  console.log(i); //error, biến `i` chưa được định nghĩa
}
function foo() {
  {
    console.log(hi); //error, biến `hi` chưa được định nghĩa

    var hi = 1;
  }
  console.log(hi); //output: 1

  for(var i = 0; i < 10; i++) {
  }

  console.log(i); //output: 10
}

Arrow function

Cú pháp mới arrow function cho phép chúng ta khai báo function ngắn gọn hơn hẳn đồng thời tăng tính cơ động linh hoạt cho javascript. Với cách viết này chúng ta có thể lược bớt việc viết liên tục từ khóa function hay câu lệnh return

ES5:

odds  = evens.map(function (v) { return v + 1; });
pairs = evens.map(function (v) { return { even: v, odd: v + 1 }; });
nums  = evens.map(function (v, i) { return v + i; });

ES5:

odds  = evens.map(v => v + 1)
pairs = evens.map(v => ({ even: v, odd: v + 1 }))
nums  = evens.map((v, i) => v + i)

Lexical this

Việc sử dụng this trong javascript nhiều lúc khá phức tạp và rối rắm khi các function được viết lồng nhau và chúng ta phải để ý phạm vi ảnh hưởng của this. Mỗi function trong javascript định nghĩa 1 context this nên việc phải để tâm this này với this kia khá là phiền. Ví dụ bạn muốn thay đổi text của DOM theo thời gian từng giây với đoạn code như sau

$('.current-time').each(function () {
  setInterval(function () {
    $(this).text(Date.now());
    }, 1000);
    });

Đoạn code trên sẽ cho kết quả không đúng ý bạn do this ở đây là context của setInterval chứ không phải của element cần được change text. Cách tiếp cận thường thấy là xài thêm 1 biến that.

$('.current-time').each(function () {
  var that = this;

  setInterval(function () {
    $(that).text(Date.now());
    }, 1000);
    });

Tuy nhiên trong ES6 chúng ta có cách xử lí hay hơn là dùng arrow function. Lí do là arrow function không tạo ra this context.

$('.current-time').each(function () {
  setInterval(() => $(this).text(Date.now()), 1000);
  });

Xử lí params mở rộng

Giá trị params mặc định

Việc cho phép khai báo giá trị params mặc định giúp loại bỏ các xử lí kiểm tra đầu vào cũng như tiện cho việc truyền tham số cho function

ES5:

function f (x, y, z) {
  if (y === undefined)
  y = 7;
  if (z === undefined)
  z = 42;
  return x + y + z;
};
f(1) === 50;

ES6:

function f (x, y = 7, z = 42) {
  return x + y + z
}
f(1) === 50

Rest param

Bên cạnh việc cho phép khai báo giá trị params mặc định thì việc cho phép khai báo danh sách param còn lại ngoài các params chính cũng là 1 điểm hay của ES6

ES5:

function f (x, y) {
  var a = Array.prototype.slice.call(arguments, 2);
  return (x + y) * a.length;
};
f(1, 2, "hello", true, 7) === 9;

ES6:

function f (x, y, ...a) {
  return (x + y) * a.length
}
f(1, 2, "hello", true, 7) === 9

Class

Javascript ES5 không hỗ trợ class như các ngôn ngữ lập trình hướng đối tượng khác. Thay vào đó, Javascript mô phỏng các class thông qua các function và prototype. Tuy nhiên với ES6 bạn đã có thể lập trình hướng đối tượng với class trên Javascript. ES6 hỗ trợ việc thừa kế, lời gọi super tới class cha, static method, contructor.

class Project {
  constructor(name) {
    this.name = name;
  }
  start() {
    return "Project " + this.name + " starting";
  }
}

var project = new Project("A");
console.log(project.start());

=> Project A starting
class RubyProject extends Project {
  constructor(name, from) {
    super(name);
    this.from = from;
  }

  info() {
    return this.name + " has begun from " + this.from;
  }
}

var ruby_project = new RubyProject("B", "1-1-2015");

console.log(ruby.start());
console.log(ruby_project.info());

=> Project B starting
B has begun from 1-1-2015

Kết

Để đảm bảo tương thích với các trình duyệt khác nhau khi phát triển web việc code javascript sử dụng các tính năng bám sát đặc tả ECMAScript là cần thiết. Ngoài ra cập nhật và tận dụng các tính năng mới của ECMAScript sẽ giúp viết code ngắn gọn và tiện lợi hơn.