Javascript ES6: Điểm mặt tính năng

Javascript ES6 mang đến nhiều cú pháp cũng như những tính năng vô cùng tuyệt vời, giúp code của bạn trông có vẻ hiện đại, dễ đọc hơn bao giờ hết. Có thể ví von ES6 giúp viết ít làm được nhiều (write less do more). ES6 giới thiệu nhiều tính năng mới như hàm arrow (arrow function), template string, khai báo class, mô-đun và… nhiều hơn nữa. Hôm nay chúng ta sẽ cùng tìm hiểu sơ qua nhé.

constlet

Đầu tiên là const, đó là một từ khóa mới trong ES6 giúp khai báo biến. Tuy nhiên const có nhiều điểm “ghê gớm” hơn var rất nhiều. Đó là khi bạn đã khai báo giá trị cho biến rồi thì không thể khai báo lại một nữa. Nói một cách khác nó là hằng số bất biến, tuy nhiên điều này ngoại trừ object nhé. Cùng check một ví dụ để hiểu hơn về nó nhé.

JAVASCRIPT
1
2
3
4
5
const myVariable = 5;

// Nếu bạn thử reassign giá trị mới cho myVariable thì nó sẽ báo lỗi
myVariable = 10; // sẽ xuất hiện lỗi tại đây -> Uncaught TypeError: Assignment to constant variable.

Vậy còn ngoại trừ object là như thế nào:

JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
const myObject = {
name: 'Thang',
age: 23
};

// Bạn có thể assign giá trị mới
myObject.age = 22;

// Add thêm thuộc tính
myObject.gender = 'Male';

console.log(myObject); // -> Kết quả là Object trên được trả về

Trong trường hợp trên nếu bạn viết:

JAVASCRIPT
1
const myObject = 5; // error

Thì sẽ xuất hiện lỗi, bởi vì giá trị ban đầu là object và bạn vừa assign cho nó một số nguyên, nó đã bị gán lại.

Từ đây các bạn có thể suy ra, Javascript sử dụng kiểu tham chiếu địa chỉ khi khai báo một biến. Ví dụ khi tạo biến myObject Javascript sẽ tạo ra một vùng nhớ chứa giá trị của myObject, địa chỉ vùng nhớ này được gán cho biến myObject và khi sử dụng từ khóa const thì bạn không được phép thay đổi giá trị mới hay nói cách khác là không trở myObject tới vùng nhớ khác được nữa. Trong trường hợp object, việc bạn thay đổi giá trị thuộc tính của object thực ra bạn đang thay đổi giá trị tại vùng nhớ kia mà thôi.Tóm lại const chỉ quan tâm bạn không được thay đổi địa chỉ mới là được.

Vậy nên dung const trong trường hợp nào. Theo mình nó đặc biệt hữu ích khi dùng để gán giá trị là một target element của html. Ví dụ là khi bạn có một label, label này dùng khi bạn muốn chèn đoạn văn bản nào đấy.

JAVASCRIPT
1
2
3
4
5
// thời xưa :v
var myLabel = document.getElementById('myLabel');

// ES6
const myLabel = document.getElementById(‘myLabel’);

Tại sao nó lại nên dùng khi gán giá trị là một phần tử của html mà không dùng var? Thứ nhất nay hiện đại rồi, không ai xài var nữa :v thứ hai var có đặc tính là hoisting, scope của nó là function scope, còn với const là blocked scope. Nếu bạn có hiểu qua hoisting rồi thì sẽ hiểu ra chỗ này.
Vậy tại sao không dùng let? Đơn giản hãy để ý, bạn có muốn assign giá trị mới label của bạn không, id trên HTML thì độc nhất rồi, vậy assign lại làm gì nữa. Vậy cách tốt nhất trong trường hợp này là sử dụng const 😊)

Về let, let cho phép bạn gán giá trị khác cho một biến đã khai báo, khác với const không cho phép điều đó. Một điểm let giống const đó là scope của nó là blocked scope.

Scope thì bạn có thể tìm hiểu them trên mạng nhé. Đại khái là như vầy nè (chạy đoạn code này trên trình duyệt thì sẽ hiểu).

JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function globalFunc() { // đây là function scope
var myVar = 5;
let myLet = 3;

if (document) { // đây là blocked scope
var myVar = 10;
let myLet = 7;
}

console.log(myVar); // kết quả là 10;
console.log(myLet); // kết quả là 3;
}

globalFunc();

Hàm arrow (Arrow functions)

Với tính năng này mình chỉ biết khen thôi, nó thật sự tuyệt zời, trong các feature thì mình thích nó nhất đấy. Code sử dụng arrow function thì sẽ dễ đọc, dễ tổ chức và nhìn có vẻ hiện đại rất nhiều. Thay vì dùng kiểu truyền thống thì chuyển sang dùng arrow sẽ vui hơn đấy.

JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// khai báo function thời khủng long
function sayHello(name) {
return 'Hello ' + name;
}

// gọi hàm
console.log(myFunction('Thang'));

// khai báo thời hiện đại
// Nếu code chỉ có một dòng thì nó sẽ tự động return giá trị về cho bạn
const myFunction = name => 'Hello ' + name;

// or
const myFunction = name => {
// … to do something
return 'Hello ' + name;
}

// nhiều parameter
const myFunction = (name, age = 22) => {
return 'I am ' + name + ', ' + age + ' years old!';
}

// gọi
console.log(myFunction('Thang'));

Sơ qua vậy thì bạn có thể thấy có tuyệt zời không :D, viết ít làm ngang ngửa có khi nhiều hơn cái thèn cũ ấy chứ.

Thêm một ví dụ nữa để bạn thấy arrow function hữu ích là sử dụng nó trong các hàm như map, filterreduce của array.

JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
const names = ['A', 'B', 'C', 'D'];

// ES5
let clone1 = names.map(function (item) {
return item;
});

console.log(clone1); // In ra ['A', 'B', 'C', 'D']

// ES6
let clone2 = name.map(item => item);

console.log(clone2); // Sẽ in ra ['A', 'B', 'C', 'D']

Hai function kia cũng tương tự vậy. Code như vậy mình nghĩ trông sẽ đẹp hơn, dễ đọc và ngắn gọn rất nhiều

  • Lưu ý:
JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
const me = {
name: 'Thang',

age: 22,

showInfo: function () {
console.log('I am ' + this.name + ', ' + this.age + ' years old!');
}
};

// In ra kết quả bình thường
me.showInfo(); // I am Thang, 22 years old!

Nhưng nếu bạn viết lại như vầy:

JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
const me = {
name: 'Thang',

age: 22,

// Thay chỗ này bằng arrow functions cho nó ngầu lòi
showInfo: () => {
console.log('I am ' + this.name + ', ' + this.age + ' years old!');
}
};

// Kết quả in ra là
me.showInfo(); // I am , undefined years old!

this trong function thường sẽ là this của object gọi nó, còn this trong arrow function sẽ là this bên ngoài nó, nói cách khác, nó không có this, nó lấy this bên ngoài mà xài.

Tuy nhiên sửa lại

JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Me {
constructor() {
this.name = 'Thang';

this.age = 22;

// sử dụng arrow function nè
this.showInfo = () => {
console.log('I am ' + this.name + ', ' + this.age + ' years old!');
}
}
};

const me = new Me();

// Kết quả in ra là
me.showInfo(); // I am Thang, 22 years old!

Rút ra là khi sử dụng kiểu hiện đại thì theo luôn hiện đại và quan trọng hiểu rõ bản chất nhé.

Template Literals

Cái tên này mình quen gọi vậy, nó thuộc kiểu từ ngữ chuyên ngành không nên dịch ra, dịch ra nghe chuối lắm, hiểu là được rồi :v.
Bạn có để ý thấy ở các ví dụ dưới có chi rắc rối loằng ngoằn không.

JAVASCRIPT
1
2
3
4
5
6
var name = 'Thang'
age = 22;

showInfo() {
console.log('I am ' + name + ', ' + age + ' years old!');
}

Hic đó là cộng chuỗi trong console @@. Cái ni hồi xưa mình làm và hay bị lỗi thiếu dấu cộng đôi lúc thiếu luôn dấu ' hoặc " :v. May thay ES6 giới thiệu một giải pháp thay thế, một giải pháp cho người hay loạn mắt (như mình).

JAVASCRIPT
1
2
3
4
5
6
7
// ES6
let name = 'Thang'
age = 22;

showInfo() {
console.log(`I am ${name}, ${age} years old!`);
}

Ôi cha cha, thật dễ nhìn.

Giá trị tham số mặc định

Thực ra ở một ví dụ bên trên mình đã sử dụng cái này rồi

JAVASCRIPT
1
2
3
4
5
6
// age có giá trị mặc định là 22
const myFunction = (name, age = 22) => {
return `I am ${name}, ${age} years old!`;
}

console.log(myFunction('Thang')); // I am Thang, 22 years old!

Khác với ngôn ngữ khác, khi không truyền giá trị của tham số vào Javascipt tự động “ném” vào một giá trị mặc định t và nó không sinh ra lỗi gì cả, xem ví dụ là hiểu nè.

JAVASCRIPT
1
2
3
4
5
6
// age có giá trị mặc định là 22
const myFunction = function (name, age) {
return 'I am ' + name + ', ' + age + ' years old!';
}

console.log(myFunction('Thang')); // I am Thang, undefined years old!

Class

Như đã show trong ví dụ của arrow functions, class là một tính năng mới của ES6, trong lập trình hướng đối tượng OOP, class chính là cái core. Nó giúp code của bạn nhìn có tổ chức và đảm bảo tính an toàn cũng như tính đóng gói của các biến.

Cấu trúc một class cơ bản như sau:

JAVASCRIPT
1
2
3
4
5
class MyClass {
constructor() {

}
}

Ví dụ:

JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// sử dụng từ khóa `class` để khai báo một class
class Human {
// Hàm tạo
constructor(name, age) {
this.name = name;
this.age = age;
}

// một method
showInfo() {
console.log(`I am ${this.name}, ${this.age} years old!`);
}
}

// Để sử dụng class trên ta dùng từ khóa `new`
const me = new Human('Thang', 22);

// gọi một method trong class
me.showInfo();

Thừa kế từ class khác ta dùng extends

JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Student extends Human {
// Ví dụ về getter and setter
setGrade(grade) {
this.grade = grade;
}

getGrade() {
return this.grade;
}
}

const me = new Student('Thang', 18);

me.showInfo();

me.setGrade(12);

console.log(me.getGrade());

Bạn có thể đọc thêm về class tại đây https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes.

Array và Object destructing

Với tính năng này việc “bóc tách” thuộc tính hay value từ array hoặc object ra nhanh hơn bao giờ hết.

Ví dụ destructing với object

JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const contact = {
name: 'Ronaldo',
age: 33,
nickname: 'CR7',
team: 'Juventus'
};

// hồi xưa
let name = contact.name;
let age = contact.age;
let nickname = contact.nickname;
let team = contact.team;

// ngày nay dùng destructing thì nhanh như vầy
let { name, age, nickname, team } = contact;

console.log('Name', name);
console.log('Age', name);
console.log('Nickname', name);
console.log('Team', name);

Vậy nếu mình destructing cái thuộc tính không có thì sao, ví dụ:

JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
const contact = {
name: 'Ronaldo',
age: 33,
nickname: 'CR7',
team: 'Juventus'
};

let { name, address } = contact;

console.log('Name', name);
console.log('Address', address); // undefined sẽ được return ra đây

Kết quả nếu destructing một thuộc tính không có trong object, thì mặc định undefined sẽ được trả về. Vì vậy chúng ta phải luôn đảm bảo tên biến và tên thuộc tính giống nhau. Vậy lỡ tao không muốn dùng tên thuộc tính đó thì làm thế nào, không lẽ tao phải quay về cách hồi xưa ông bà dùng :(. À sorry tao quên mất, còn một cách khác là mày dùng dấu : (hai chấm) để thay đổi tên, ví dụ như sau:

JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
const contact = {
name: 'Ronaldo',
age: 33,
nickname: 'CR7',
team: 'Juventus'
};

// thay đổi tên thuộc tính với tên biến khác
let { name, team:footballTeam } = contact;

console.log('Name', name); // Name Ronaldo
console.log('Player of', footballTeam); // Player of Juventus

Đối với array thì cũng tương tự, ta thay dấu ngoặc nhọn bằng dấu ngoặc vuông là được. Cơ mà không cần tên biến phải giống key của array, destructing của array sẽ xác định dựa vào thứ tự trong mảng đó.

JAVASCRIPT
1
2
3
4
5
6
7
8
const players = ['Ronaldo', 'Messi', 'Neymar', 'Mbappe'];

let [player1, player2, player3, player4] = players;

console.log(player1);
console.log(player2);
console.log(player3);
console.log(player4);

Khác với object bên này nếu destructing ra mà quá array, ví dụ bạn thêm vào player5 thì lỗi sẽ được bắn ra.

Rest parameter và toán tử Spread

Rest parameter

Rest parameter dịch ra là tham số còn lại, dịch ra nghe chuối quá thôi để yên cho nó hay. Nhiệm vụ của nó là lấy đối số truyền vào mà không xác định trước được số lượng và trả về một array, ví dụ:

Bạn muốn hàm sum tính tổng 100 số do người dùng nhập vào. Vấn đề ở đây là không lẽ tạo ra 100 tham số cho hàm đó với lại nhỡ người dùng thích tính tổng 20 số thôi thì sao? Trong phạm vi bài viết này tui sẽ show bạn cách dùng rest parameter để giải quyết vấn đề này.

Cách giải quyết mang họ chuối (thực ra không ai viết thế này đâu, họ dùng arguments cơ mà ví dụ này tui làm vậy cho nó thực tế :v):

JAVASCRIPT
1
2
3
4
5
// setup 100 tham số vào đây
function sum(a, b, c, d, ..., n ) {
// cộng hết 100 số lại
return a + b + c + d + ... + n;
}

Cách giải quyết mang tính thời đại

JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
function sum(a, b, ...n ) {
// n lúc này là một array và chứa các tham số còn lại được truyền vào

// bạn có thể dùng vòng `for` thay cho `reduce`
return n.reduce((previous, current) => {
return previous + current;
});
}

console.log(sum(1, 2, 3, 4, 5, 6, 7)); // in ra 25

Cú pháp:

JAVASCRIPT
1
2
3
function f(a, b, ...theArgs) {
// do something
}

Có thể áp dụng rest parameter để destructing đối số truyền vào.

JAVASCRIPT
1
2
3
4
5
6
7
function sum(...[a, b, c]) {
return a + b + c;
}

console.log(sum(1)) // NaN bởi vì b với c có nên chúng là `undefined`
console.log(sum(1, 2, 3)) // 6
console.log(sum(1, 2, 3, 4)) // 6 bởi vì tham số thứ 4 không được destruct ra

Vậy nếu không truyền đối số vào cho rest parameter thì như thế nào :)), lúc này thì rest parameter sẽ vẫn là một array nhưng là array có length bằng 0.

JAVASCRIPT
1
2
3
4
5
6
function f(a, b, ...theArgs) {
console.log(theArgs);
console.log(theArgs.length);
}

f(1, 2);

Toán tử spread

Còn ở bên kia căn phòng, toán tử có cú pháp giống hệt “rest parameter”. Nhiệm vụ của spread là lấy các đối số vào cho nó sau đó “rải” hay “banh tách” các đối số này ra.

JAVASCRIPT
1
2
3
4
5
6
7
8
const numbers = [1, 2, 3];

function sum(a, b, c) {
return a + b + c;
}

// rải các value trong mảng `numbers` vào đối số
console.log(sum(...numbers));

Nối mảng như siêu nhân.

JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
const parts = ['shoulders', 'knees']; 
const lyrics = ['head', ...parts, 'and', 'toes'];

console.log(lyrics); // ["head", "shoulders", "knees", "and", "toes"]

let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1 = [...arr1, ...arr2];

console.log(arr1); // [0, 1, 2, 3, 4, 5]

Còn nhiều cái hay ho với cái này nữa, bạn có thể tham khảo tại link sau https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax.

Tóm lược

Nhìn sơ qua hai cái này dễ gây nhầm lẫn, nhất là chỗ destructing, có người A bảo rest paramater dùng để destructing được like that:

JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
const arr = [1, 2, 3, 4, 5, 6, 7];

let [value1, value2, ...rest] = arr;

console.log(value1);
console.log(value2);

for (let i of rest) {
console.log(rest)
}

Rồi nhiều người B, C khác nhìn vào bảo đây là spread ra nè, giống spread quá chừng mà.

Người A bảo vậy không sai :)) nhưng họ đi quá nhanh thành ra dễ gây rối và nhầm lẫn cho người mới bắt đầu, bởi vì ri rest parameter lấy đối đối số truyền vào, truyền vào đâu, truyền vào hàm hoặc … cái chi mà cần đối số truyền vào :v. Như ví dụ trên destructing mảng này cần đối số truyền vào để nhận giá trị sau khi destruct ra. Giả sử mảng arr có length là 100 không lẽ bạn viết 100 đối số nhận giá trị like that:

JAVASCRIPT
1
let [value1, value2, ..., value100] = arr;

lúc này cái bạn cần là một đối số có khả năng gom các giá trị đã được destruct ra, chỉ có rest parameter thỏa chỗ này. Việc destructing ra thì nó giống spread nó sẽ tách ra sau đó nhờ rest parameter gom lại giúp nó. Túm lại chỗ này rest paramater dùng để destructing là chính xác nhé.

Rest parameter ngược lại với spread, rest thì “gom”, “tích góp” lại còn spread thì “tách”, “rải”, “phá”, “banh” ra.

Import và export

Hai từ khóa này là cải tiến đáng kể giúp cho javascript mạnh hơn bao giờ hết. Chúng cho phép bạn chia nhỏ code ra thành các thành phần và tái sử dụng.

Cách hoặc động của chúng khá đơn giản export dùng để xuất module (mô đun có thể là biến, mảng object, class …) ra sau đó import sẽ nhận module đó để sử dụng.

Ví dụ tui có 2 file. Một file tên là detailComponent.js và file còn lại tên là homeComponent.js.

Trong file detailComponent.js tui code một function và muốn export function đó ra, tui viết như sau.

JAVASCRIPT
1
2
3
export default function detail(name, age) {
console.log(`I am ${name}, ${age} years old!`);
}

Trong file homeComponent.js dùng import để sử dụng.

JAVASCRIPT
1
2
3
import detail from './detailComponent'

detail('Thang', 23);

Nếu muốn export nhiều và import nhiều module thì có thể viết lại như sau:

File detailComponent.js.

JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
exports.detail = function (name, age) {
console.log(`I am ${name}, ${age} years old!`);
}

exports.showName = function (name) {
console.log(`I am ${name}`);
}

exports.showAge = function (age) {
console.log(`I am ${age} years old!`);
}

File homeComponent.js.

JAVASCRIPT
1
2
3
4
5
import { detail, showName, showAge } from './detailComponent'

detail('Thang', 23);
showName('Thang');
showAge(23);

Promise

Promises are a new feature of ES6. It’s a method to write asynchronous code. It can be used when, for example, we want to fetch data from an API, or when we have a function that takes time to be executed. Promises make it easier to solve the problem, so let’s create our first Promise!

Promise là một tính năng mới trong ES6 nó giúp bạn viết code bất đồng bộ tốt hơn. Vậy cần dùng promise trong trường hợp nào và dùng nó như thế nào? Ví dụ khi bạn muốn lấy data từ API, việc request tới server API tốn một khoảng thời gian, lúc này bạn cần dùng promise để khi mà API trả về xong, thì promise “báo” cho bạn biết là request đã oke hay không. Cùng viết thử promise xem sao nè.

JAVASCRIPT
1
2
3
4
5
6
7
const myPromise = () => {
return new Promise((resolve, reject) => {
resolve('Promise ni đã chạy thành công')
});
}

console.log(myPromise()); // Sẽ in ra Promise {<resolved>: "Promise ni đã chạy thành công"}

Nếu nhìn vào console log bạn sẽ thấy nó return về một Promise, cơ mà tui muốn in ra dòng Promise ni đã chạy thành công thôi mà, in ra rứa thì làm ăn được chi đây. Promise nhận 2 tham số truyền vào callback là resolvereject. Resolve handle kết quả nếu thành công, reject thì sẽ xử lý lỗi khi chạy có lỗi.

Ví dụ:

JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
console.log('Starting request lấy data từ API');

const url = 'https://jsonplaceholder.typicode.com/posts';

const getData = (url) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();

xhr.addEventListener("load", function () {
resolve(this.response);
});

xhr.addEventListener("error", function () {
reject(new Error('Có lỗi xảy ra'));
});

xhr.open('GET', url);
xhr.send();
})
}

getData(url)
.then(data => {
console.log(data);
})
.catch(error => {
console.error(error);
});

Bây giờ nhìn vào console log thì kết quả là json của API kia được show ra.

ES6 thật sự mà nói có nhiều tính năng thật tuyệt vời, bạn có thể xem thêm tại đây.

Tổng kết

Mặc dù nay đã ra ES7, … đồ rồi, cơ mà ES6 vẫn được xem là một bước tiến lớn của Javascript, nên mình hi vọng các bạn thấy bài viết này hữu ích. Hi vọng nhận được sự ủng hộ, để thời gian mình cố gắng hơn. Cảm ơn bạn đã đọc bài viết. :))

Bài viết có tham khảo từ: https://medium.freecodecamp.org/write-less-do-more-with-javascript-es6-5fd4a8e50ee2

 Comments
Comment plugin failed to load
Loading comment plugin