Từ phiên bản ES5 (2009) JavaScript đã có những điểm sáng. Nhưng phiên bản ES6 (2015) thực sự mang lại một thay đổi rất đáng kể.
ES6 là gì?
ECMAScript 2015 (hay gọi tắt là ES6) là phiên bản Thứ Sáu của ECMAScript. Nó xác định tiêu chuẩn cho việc triển khai JavaScript (Hiện là ECMAScript 2020)
ES6 đã mang lại những thay đổi rất đáng kể cho ngôn ngữ JavaScript.
Nó giới thiệu một số tính năng mới như, các biến phạm vi khối, vòng lặp mới để lặp qua các mảng và đối tượng và nhiều cải tiến khác để giúp lập trình JavaScript dễ dàng và thú vị hơn.
Đặc biệt, các thư viện / framework hiện đại của JavaScript như React.js, Angular, Vue rất hay sử dụng các tính năng mới này.
Thế nên, trong bài này, hãy cùng tìm hiểu về 11 tính năng của ES6 (tính năng nổi bật nhất), chúng có thể sẽ giúp bạn học tập và làm việc đơn giản hơn rất nhiều.
#1: TỪ KHÓA let
ES6 giới thiệu từ khóa let
để khai báo các biến. Trước ES6, cách duy nhất để khai báo một biến trong JavaScript là từ khóa var
.
Vậy sự khác biệt giữa chúng là gì?
Có hai điểm khác biệt quan trọng giữa var và let:
-
Các biến được khai báo với từ khóa
var
có phạm vi trong hàm (function scope) và hành vi hoisting trong JavaScript xảy ra (nâng phần khai báo lên trên cùng trong phạm vi của nó).
-
Trong khi các biến được khai báo với từ khóa
let
là phạm vi khối { }
(block scope). Bạn bắt buộc phải khai báo rõ ràng trước khi sử dụng chúng. Và không có hoisting ở đây.
Ví du, trong ES5 thì:
// Cú pháp ES5
for (var i = 0; i < 5; i++) {
console.log(i); // 0,1,2,3,4
}
console.log(i); // 5
Chúng ta có thể truy cập biến i
mặc dù nó được khai báo bên trong vòng lặp for.
Nhưng ở trong ES6:
// Cú pháp ES6
for (let i = 0; i < 5; i++) {
console.log(i); // 0,1,2,3,4
}
console.log(i); // undefined
Như bạn có thể thấy trong ví dụ trên, biến i
không thể truy cập được bên ngoài vòng lặp for.
Điều này cũng cho phép chúng ta sử dụng lại cùng một tên biến nhiều lần vì phạm vi của nó bị giới hạn trong khối { }
, dẫn đến khai báo ít biến hơn và code sạch hơn.
#2: TỪ KHÓA const
Từ khóa const
mới được giới thiệu trong ES6 giúp bạn có thể định nghĩa các hằng số.
Hằng số ở chế độ chỉ đọc, có nghĩa là bạn không thể gán lại giá trị mới cho chúng.
Chúng cũng được phân chia theo phạm vi khối giống như let.
// Khai báo & Khởi tạo Hằng số PI
const PI = 3.14;
console.log(PI); // 3.14
// Cố tình gán lại giá tri cho Hằng số xem thế nào
PI = 10; // error
Tuy nhiên, bạn vẫn có thể thay đổi thuộc tính đối tượng:
// Thay đổi giá trị thuộc tính của đối tượng
const COMPANY = { name: "NIIT", age: 18 };
console.log(COMPANY.age); // 18
PERSON.age = 19;
console.log(COMPANY.age); // 19
Hoặc thay đổi phần tử mảng:
// Thay đổi phần tử trong mảng
const COLORS = ["Xanh", "Đỏ", "Tím", "Vàng"];
console.log(COLORS[0]); // Xanh
// Gán lại giá trị cho phần tử đầu tiên
COLORS[0] = "Hồng";
console.log(COLORS[0]); // Hồng
#3: VÒNG LẶP for of
Vòng lặp for of
mới được thiết kế cho phép chúng ta lặp qua mảng hoặc lặp qua đối tượng có thể lặp khác rất dễ dàng.
Ngoài ra, code bên trong vòng lặp được thực thi cho từng phần tử của đối tượng có thể lặp lại.
Đây là một ví dụ:
// Lặp qua mảng
let letters = ["a", "b", "c", "d", "e", "f"];
for (let letter of letters) {
console.log(letter);
}
Kết quả:
a
b
c
d
e
f
Và, lặp qua chuỗi (đối tượng có thể lặp):
// Lặp qua chuỗi
let greet = "Hế lô";
for (character of greet) {
console.log(character);
}
Kết quả:
H
ế
l
ô
Vòng lặp for of
không hoạt động với các đối tượng thông thường vì chúng không thể lặp lại.
Nếu bạn muốn lặp qua các thuộc tính của một đối tượng, bạn có thể sử dụng vòng lặp for in
.
#4: TEMPLATE LITERALS
Template literals giúp chúng ta dễ dàng và rõ ràng để tạo chuỗi nhiều dòng và thực hiện nội suy chuỗi.
Nó giúp chúng ta có thể nhúng các biến hoặc biểu thức vào một chuỗi tại bất kỳ vị trí nào mà không gặp bất kỳ rắc rối nào.
Các template literals được tạo bằng cách sử dụng ký tự ` `
(Gần nút ESC
đó) thay vì các dấu nháy kép " "
hoặc nháy đơn ' '
thông thường.
Các biến hoặc biểu thức có thể được đặt bên trong chuỗi bằng cú pháp ${...}
.
Hãy xem và so sánh các ví dụ sau để thấy nó hiệu quả như thế nào.
Trong ES5:
// Chuỗi nhiều dòng
var str = 'Mình muốn có một chuỗi\n\t' +
'ở trên nhiều dòng như thế này!';
// Tạo chuỗi sử dụng biến và biểu thức
var a = 6;
var b = 9;
var result = 'Tổng của ' + a + ' và ' + b + ' là: ' + (a + b) + '.';
console.log(result); // Tổng của 6 và 9 là: 15.
Trong ES6:
// Chuỗi nhiều dòng
let str = `Mình muốn có một chuỗi
ở trên nhiều dòng như thế này!`;
// Tạo chuỗi sử dụng biến và biểu thức
let a = 6;
let b = 9;
let result = `Tổng của ${a} và ${b} là: ${a+b}.`;
console.log(result); // Tổng của 6 và 9 là: 15.
Bạn thấy không, code thực sự trông đẹp hơn, dễ đọc hơn nhiều.
#5: GIÁ TRỊ MẶC ĐỊNH CHO THAM SỐ
Bây giờ, trong ES6, bạn có thể chỉ định các giá trị mặc định cho các tham số hàm.
Điều này có nghĩa là nếu không có đối số nào được cung cấp cho hàm khi nó được gọi thì các giá trị tham số mặc định này sẽ được sử dụng. Đây là một trong những tính năng từng được mong đợi nhất trong JavaScript.
Nếu như trước đây:
function sayHello(name) {
var name = name || 'NIIT';
return 'Xin chào ' + name + '!';
}
console.log(sayHello()); // Xin chào NIIT!
console.log(sayHello('Đen Vâu')); // Xin chào Đen Vâu!
Thì trong ES6, bạn có thể làm thế này:
function sayHello(name = "NIIT") {
var name = name;
return `Xin chào ${name}!`;
}
console.log(sayHello()); // Xin chào NIIT!
console.log(sayHello('Đen Vâu')); // Xin chào Đen Vâu!
#6: ARROW FUNCTION
Arrow Function là một tính năng thú vị khác trong ES6. Nó cung cấp cú pháp ngắn gọn hơn để viết biểu thức hàm bằng cách bỏ đi từ khóa function
và return
.
Các Arrow Function được định nghĩa bằng cú pháp mới, ký hiệu suy ra =>
.
Hãy xem ví dụ:
// Biểu thức hàm (Function Expression)
var sum = function(a, b) {
return a + b;
}
console.log(sum(2, 3)); // 5
// Arrow function
var sum = (a, b) => a + b;
console.log(sum(2, 3)); // 5
Như bạn thấy trong arrow function ở trên, không có từ khóa function
và từ khóa return
trong khai báo hàm.
Bạn cũng có thể bỏ luôn dấu ngoặc đơn ()
trong trường hợp có chính xác một tham số.
// Arrow function
var square = x => x * x;
console.log(square(5)); // 25
Nhưng bạn sẽ luôn cần sử dụng nó khi bạn có 0 hoặc nhiều hơn một tham số.
Ngoài ra, nếu có nhiều hơn một biểu thức trong thân hàm, bạn cần đặt nó trong dấu ngoặc nhọn { }
.
Trong trường hợp này, bạn cũng cần sử dụng câu lệnh return
để trả về một giá trị.
Có một số biến thể về cách bạn có thể viết các arrow function. Và dưới đây là những cách thường được sử dụng nhất:
// Một tham số, Một câu lệnh
var greet = name => console.log("Xin chào " + name + "!");
greet("NIIT"); // Xin chào NIIT!
// Nhiều tham số, Một câu lệnh
var multiply = (x, y) => x * y;
console.log(multiply(6, 9)); // 54
// Một tham số, Nhiều câu lệnh
var test = age => {
if (age > 18) {
console.log("Đủ tuổi xyz");
} else {
console.log("Cấm xyz");
}
}
test(21); // Đủ tuổi xyz
// Nhiều tham số, Nhiều câu lệnh
var divide = (x, y) => {
if (y !== 0) {
return x / y;
}
}
console.log(divide(10, 2)); // 5
// Không tham số, Một câu lệnh
var hello = () => console.log('Xin chào ES6');
hello(); // Xin chào ES6!
Có một sự khác biệt quan trọng giữa các hàm thông thường và arrow function.
Không giống như một hàm bình thường, arrow function không có this
của riêng nó, nó lấy this
từ hàm bên ngoài (bao quanh nó) nơi nó được định nghĩa (Trong JavaScript, this
là ngữ cảnh thực thi hiện tại của một hàm).
Để hiểu rõ điều này, hãy xem các ví dụ sau:
function Person(fullName, age) {
this.fullName = fullName;
this.age = age;
this.getInfo = function() {
// Ngữ cảnh bên ngoài hàm (Đối tượng Person)
return function() {
// Ngữ cảnh bên trong hàm (Đối tượng 'Window')
console.log(this.constructor.name); // Window
console.log("Tôi là " + this.fullName + ". " + this.age + " tuổi");
};
}
}
var p = new Person('Linh Trang', 18);
var printInfo = p.getInfo();
printInfo(); // Tôi là undefined. undefined tuổi
Như ta thấy, p.getInfo()
đang có bối cảnh thực thi hiện tại ở ngoài (Window) nên this
lúc này đề cập đến Window.
Viết lại cùng một ví dụ bằng cách sử dụng các template literals và arrow function của ES6:
function Person(fullName, age) {
this.fullName = fullName;
this.age = age;
this.getInfo = function() {
// Ngữ cảnh bên ngoài hàm (Đối tượng Person)
return () => {
// Ngữ cảnh bên trong hàm (Đối tượng Person)
console.log(this.constructor.name); // Person
console.log (`Tôi là ${this.fullName}. ${this.age} tuổi`);
};
}
}
var p = new Person('Linh Trang', 18);
var printInfo = p.getInfo();
printInfo(); // Tôi là Linh Trang. 18 tuổi
Như bạn có thể thấy rõ, từ khóa this
trong ví dụ trên đề cập đến ngữ cảnh của hàm bao quanh arrow function là đối tượng Person
, không giống như ví dụ trước đề cập đến đối tượng toàn cục Window
.
#7: CLASSES
Trong ES5 trở về trước, các class chưa bao giờ tồn tại trong JavaScript.
Các class được giới thiệu trong ES6 trông tương tự như các class trong các ngôn ngữ hướng đối tượng khác, chẳng hạn như Java, PHP ... tuy nhiên chúng không hoạt động giống hệt nhau.
> Tham khảo: HỌC LẬP TRÌNH JAVA CƠ BẢN
Các class trong ES6 giúp tạo các đối tượng, thực hiện kế thừa dễ dàng hơn bằng cách sử dụng từ khóa extends
và tái sử dụng code.
Trong ES6, bạn có thể khai báo một class bằng cách sử dụng từ khóa class
mới theo sau là tên class.
Theo quy ước, tên class được viết bằng PascalCase (tức là viết HOA chữ cái đầu tiên của mỗi từ).
> Nếu bạn chưa biết thì xem ngay cách đặt tên trong JavaScript
Hãy xem ví dụ bên dưới đây:
// Tạo một class
class Rectangle {
// Hàm tạo (constructor)
constructor(length, width) {
this.length = length;
this.width = width;
}
// Phương thức của class
getArea() {
return this.length * this.width;
}
}
Ở trên, chúng ta tạo ra một class với constructor và một phương thức tính diện tích.
Constructor để khởi tạo giá trị ban đầu cho đối tượng. Nếu bạn không tạo rõ ràng thì JavaScript cũng tự thêm một constructor rỗng.
Bây giờ, ta thực hiện kế thừa class như sau:
// Tạo class Square kế thừa class Rectangle
class Square extends Rectangle {
// constructor của class con
constructor(length) {
// Gọi đến constructor của class cha
super(length, length);
}
// Phương thức của class con
getPerimeter() {
return 2 * (this.length + this.width);
}
}
Bây giờ, ta thử khởi tạo đối tượng của class Rectangle
và tính diện tích hình chữ nhật.
// Khởi tạo đối tượng (thể hiện của class Rectangle)
let rectangle = new Rectangle(5, 10);
// Tính diện tích hình chữ nhật
console.log(rectangle.getArea()); // 50
Tiếp theo, thử tạo đối tượng từ class Square và sử dụng phương thức của nó xem nhé:
// Khởi tạo đối tượng của class Square
let square = new Square(5);
// Tính chu vi
console.log(square.getPerimeter()); // 20
Và vì Square
kế thừa Rectangle
nên nó cũng có thể sử dụng phương thức getArea()
của Rectangle
// Tính diện tích hình vuông
console.log(square.getArea()); // 25
Để chắc chắn, thử kiểm tra kiểu của các đối tượng xem thế nào nhé.
// Kiểm tra xem đối tượng vừa tạo có
// kiểu đúng với class không?
console.log(square instanceof Square); // true
console.log(square instanceof Rectangle); // true
console.log(rectangle instanceof Square); // false
Như vậy, trong ví dụ trên, class Square
đã kế thừa từ Rectangle
bằng cách sử dụng từ khóa extends
.
Các class kế thừa từ các class khác được gọi là derived classes (lớp dẫn xuất) hoặc child classes (lớp con).
Ngoài ra, bạn phải gọi super()
trong constructor của class con trước khi truy cập ngữ cảnh (this).
Ví dụ: Nếu bạn bỏ qua super()
và gọi phương thức getArea()
trên đối tượng hình square
, nó sẽ dẫn đến lỗi, vì phương thức getArea()
yêu cầu quyền truy cập vào từ khóa this
.
> Lưu ý: Không giống như khai báo hàm, khai báo lớp không được hoisting. Khai báo lớp nằm trong vùng chết tạm thời (TDZ) tương tự như khai báo let
và const
. Do đó, bạn cần khai báo lớp của mình trước khi truy cập nó, nếu không sẽ xảy ra ReferenceError
.
#8: MODULES
Trước ES6, không có hỗ trợ riêng cho các module trong JavaScript.
Mọi thứ bên trong một ứng dụng JavaScript, ví dụ như các biến trên các tệp JavaScript khác nhau, đều có chung một phạm vi.
ES6 giới thiệu tệp dựa trên module, trong đó mỗi module được biểu diễn bằng một tệp .js
riêng biệt.
Bây giờ, bạn có thể sử dụng câu lệnh export
hoặc import
trong một module để xuất hoặc nhập các biến, hàm, class hoặc bất kỳ thực thể nào khác đến / từ các module hoặc tệp khác.
Hãy thử tạo một module, tức là một tệp JavaScript main.js
và viết vào đó chương trình sau:
let greet = "Xin chào ES6!";
const PI = 3.14;
function multiplyNumbers(a, b) {
return a * b;
}
// Xuất các biến và hàm
export { greet, PI, multiplyNumbers };
Bây giờ, hãy tạo một tệp JavaScript khác app.js
và viết code như sau:
import { greet, PI, multiplyNumbers } from './main.js';
alert(greet); //Xin chào ES6!
alert(PI); // 3.14
alert(multiplyNumbers(2, 3)); // 6
Cuối cùng, tạo tệp HTML test.html
và với đoạn code sau và mở tệp HTML này trong trình duyệt của bạn.
> Lưu ý
: Để chương trình hoạt động đúng, bạn cần khai báo type = 'module'
trên thẻ <script>
.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Tính năng Module của ES6</title>
</head>
<body>
<script type="module" src="app.js"></script>
</body>
</html>
#9: REST PARAMETERS
ES6 giới thiệu Rest Parameters cho phép chúng ta truyền một số tham số tùy ý cho một hàm dưới dạng một mảng.
> Bạn cũng có thể gọi Rest Parameters là Rest Operator (Toán tử Rest)
Tham số này đặc biệt hữu ích trong các tình huống khi bạn muốn truyền các tham số cho một hàm nhưng bạn không biết chính xác mình sẽ cần bao nhiêu tham số.
Tham số nghỉ được chỉ định bằng cách thêm tiền tố tham số được đặt tên với toán tử nghỉ ...
(ba dấu chấm).
Tham số Rest chỉ có thể là tham số cuối cùng trong danh sách các tham số và chỉ có thể có một tham số Rest.
Hãy xem ví dụ sau để xem tham số Rest hoạt động như thế nào:
function sortNumbers(...numbers) {
return numbers.sort();
}
console.log(sortNumbers(3, 5, 7));
// Kết quả: [3, 5, 7]
console.log(sortNumbers(3, 5, 7, 1, 0));
// Kết quả: [0, 1, 3, 5, 7]
Khi tham số Rest là tham số duy nhất trong một hàm, nó sẽ nhận tất cả các đối số được truyền cho hàm.
Nếu không, nó sẽ nhận phần còn lại của các đối số vượt quá số tham số được đặt tên, ví dụ như sau:
function arrayNumbers(a, b, ...numbers) {
return numbers;
}
console.log(arrayNumbers(3, 5, 7, 1, 0));
// Kết quả: [7, 0, 1]
> Lưu ý: Đừng nhầm lẫn giữa các tham số Rest với REST (REpresentational State Transfer). Rest không liên quan gì đến RESTful web services.
#10: SPREAD OPERATOR
Toán tử Spread, được ký hiệu là ...
, thực hiện chức năng hoàn toàn ngược lại với toán tử Rest.
Toán tử Spread (tức là chia nhỏ) một mảng và chuyển các giá trị vào hàm được chỉ định, như được hiển thị trong ví dụ sau:
function addNumbers(a, b, c) {
return a + b + c;
}
let numbers = [2, 3, 4];
// Trong ES5
// Cách truyền mảng làm đối số của hàm
alert(addNumbers.apply(null, numbers)); // 9
// Trong ES6
// Sử dụng toán tử Spread
alert(addNumbers(...numbers)); // 9
Toán tử spread cũng có thể được sử dụng để chèn các phần tử của một mảng vào một mảng khác mà không cần sử dụng các phương thức mảng như push()
, unshift()
hay concat()
, ...
let color1 = ["Xanh", "Đỏ", "Tím"];
let color2 = ["Vàng", "Hồng"];
// Tạo một mảng mới bằng cách chèn mảng đã có
let colors = [...color1, "Nâu", "Đen", "Trắng", ...color2];
console.log(colors);
// ["Xanh", "Đỏ", "Tím", "Nâu", "Đen", "Trắng", "Vàng", "Hồng"]
Như bạn thấy đó, bạn có thể dễ dàng nhét một mảng vào một mảng khác chỉ với toán tử Spread.
#11: DESTRUCTURING ASSIGNMENT
Phép gán hủy cấu trúc là một biểu thức giúp dễ dàng trích xuất các giá trị từ mảng hoặc thuộc tính từ các đối tượng, thành các biến riêng biệt bằng cách cung cấp một cú pháp ngắn hơn.
Có hai loại phép gán hủy cấu trúc:
-
Phép gán hủy cấu trúc mảng
-
Phép gán hủy cấu trúc đối tượng.
Hãy xem cách hoạt động chính xác của từng loại trong ví dụ sau:
Phép gán phá hủy cấu trúc mảng
Trước ES6, để nhận một giá trị riêng lẻ của một mảng, chúng ta cần viết code như thế này:
// Cú pháp ES5
var colors = ["Xanh", "Đỏ"];
var a = colors[0];
var b = colors[1];
console.log(a); // Xanh
console.log(b); // Đỏ
Trong ES6, chúng ta có thể làm điều tương tự chỉ trong một dòng bằng cách sử dụng phép gán hủy cấu trúc mảng như sau:
// Cú pháp ES6
let colors = ["Xanh", "Đỏ"];
let [a, b] = colors; // Phép gán hủy cấu trúc mảng
console.log(a); // Xanh
console.log(b); // Đỏ
Bạn cũng có thể sử dụng toán tử rest trong phép gán hủy cấu trúc mảng, như được hiển thị ở đây:
// Cú pháp ES6
let colors = ["Xanh", "Đỏ", "Tím"];
let [a, ...r] = colors;
console.log(a); // Xanh
console.log(r); // ["Đỏ", "Tím"]
console.log(Array.isArray(r)); // true
Như bạn thấy đó, r
bây giờ đại diện cho mảng các phần tử trừ phần từ đầu tiên.
Phép gán phá hủy cấu trúc đối tượng
Trong ES5 để trích xuất các giá trị thuộc tính của một đối tượng, chúng ta cần code như thế này:
// Cú pháp ES5
var school = { name: "NIIT", age: 18 };
var name = school.name;
var age = school.age;
console.log(name); // NIIT
console.log(age); // 18
Nhưng trong ES6, bạn có thể trích xuất các giá trị thuộc tính của đối tượng và gán chúng cho các biến một cách dễ dàng như sau:
// Cú pháp ES6
let school = { name: "NIIT", age: 18 };
let { name, age } = school; // Phép gán hủy cấu trúc đối tượng
alert(name); // NIIT
alert(age); // 18
Hầu hết các tính năng mà chúng ta đã cùng tìm hiểu ở trên đều được hỗ trợ trong phiên bản mới nhất của các trình duyệt web chính như Google Chrome, Mozilla Firefox, Microsoft Edge, Safari, v.v.
Ngoài ra, bạn có thể sử dụng các tool chuyển đổi code miễn phí như Babel để chuyển code ES6 hiện tại của bạn sang ES5 để tương thích tốt hơn các trình duyệt khác (hoặc các phiên bản thấp hơn) mà vẫn tận dụng được lợi thế của ES6 mang lại khi viết code JavaScrpt.
Tóm lại
Trên đây là 11 tính năng của ES6 có thể mang lại cho bạn rất nhiều lợi ích, giúp quá trình lập trình web của bạn được dễ dàng hơn.
Ngoài ra, còn nhiều tính năng hữu ích khác có thể sẽ giúp ích cho bạn ở thời điểm nào đó. Xem thêm tại đây
> Nếu bạn đang tích cực học tập để làm lập trình viên Front end / Back end thì tham gia ngay KHÓA HỌC JAVA hoặc KHÓA HỌC PHP để học theo lộ trình bài bản hơn, nhanh chóng đạt mục tiêu của mình hơn.
---
HỌC VIỆN ĐÀO TẠO CNTT NIIT - ICT HÀ NỘI
Học Lập trình chất lượng cao (Since 2002). Học thực tế + Tuyển dụng ngay!
Đc: Tầng 3, 25T2, N05, Nguyễn Thị Thập, Cầu Giấy, Hà Nội
SĐT: 02435574074 - 0383.180086
Email: hello@niithanoi.edu.vn
Fanpage: https://facebook.com/NIIT.ICT/
#niit #niithanoi #niiticthanoi #hoclaptrinh #khoahoclaptrinh #hoclaptrinhjava #hoclaptrinhphp #java #php #python