ES6 cho Con Người
Hướng dẫn đầy đủ hiện đã có sẵn tại Amazon.
1. let, const và phạm vi khối
let
cho phép bạn tạo các khai báo được gắn với bất kỳ khối nào, được gọi là phạm vi khối. Thay vì sử dụng var
, mà cung cấp phạm vi hàm, nên được khuyến nghị sử dụng các biến có phạm vi khối (let
hoặc const
) trong ES6.
var a = 2;
{
let a = 3;
console.log(a); // 3
let a = 5; // TypeError: Identifier 'a' has already been declared
}
console.log(a); // 2
Một biểu thức khai báo với phạm vi khối khác là const
, tạo ra các hằng số. Trong ES6, const
đại diện cho một tham chiếu hằng số đến một giá trị. Nói cách khác, nội dung của Object
và Array
có thể thay đổi, chỉ việc gán lại biến mới được ngăn chặn. Dưới đây là một ví dụ đơn giản:
{
const b = 5;
b = 10; // TypeError: Assignment to constant variable
const arr = [5, 6];
arr.push(7);
console.log(arr); // [5,6,7]
arr = 10; // TypeError: Assignment to constant variable
arr[0] = 3; // value is mutable
console.log(arr); // [3,6,7]
}
Một số điều cần ghi nhớ:
- Hiện tượng hoisting của
let
vàconst
khác với hoisting truyền thống của biến và hàm. Cảlet
vàconst
đều được hoisting, nhưng không thể truy cập trước khi được khai báo, do Temporal Dead Zone let
vàconst
có phạm vi trong khối bao quanh gần nhất.- Khi sử dụng
const
với chuỗi hoặc giá trị cố định, CAPITAL_CASING có thể phù hợp (ví dụ:const PI = 3.14
) const
phải được định nghĩa cùng với khai báo của nó.- Luôn luôn sử dụng
const
thay cholet
, trừ khi bạn có kế hoạch gán lại biến.
2. Arrow Functions (Hàm Mũi Tên)
Arrow functions (hàm mũi tên) là một biểu thức ngắn gọn để viết hàm trong ES6. Định nghĩa hàm mũi tên bao gồm một danh sách tham số ( ... )
, tiếp theo là dấu =>
và một thân hàm. Đối với các hàm có một đối số, dấu ngoặc đơn có thể được bỏ qua.
// Classical Function Expression
function addition(a, b) {
return a + b;
};
// Implementation with arrow function
const addition = (a, b) => a + b;
// With single argument, no parentheses required
const add5 = a => 5 + a;
Lưu ý rằng trong ví dụ trên, hàm mũi tên addition
được triển khai với “thân ngắn gọn” mà không cần một câu lệnh return rõ ràng. Chú ý sự thiếu sót của { }
sau =>
.
Dưới đây là một ví dụ với “thân dạng khối” thông thường. Bao gồm các dấu ngoặc nhọn.
const arr = ['apple', 'banana', 'orange'];
const breakfast = arr.map(fruit => {
return fruit + 's';
});
console.log(breakfast); // ['apples', 'bananas', 'oranges']
Hãy xem! Còn nhiều hơn nữa…
Arrow function không chỉ làm cho mã nguồn ngắn hơn. Chúng liên quan mật thiết đến hành vi gắn kết this
.
Hành vi của arrow function với từ khóa this
khác với hàm thông thường. Mỗi hàm trong JavaScript định nghĩa ngữ cảnh this
riêng của nó, nhưng arrow function bắt giữ giá trị this
của ngữ cảnh bao quanh gần nhất. Hãy xem mã nguồn dưới đây:
function Person() {
// The Person() constructor defines `this` as an instance of itself.
this.age = 0;
setInterval(function growUp() {
// In non-strict mode, the growUp() function defines `this`
// as the global object, which is different from the `this`
// defined by the Person() constructor.
this.age++;
}, 1000);
}
var p = new Person();
Trong ECMAScript 3/5, vấn đề này đã được khắc phục bằng cách gán giá trị trong this
cho một biến có thể được đóng gói.
function Person() {
const self = this;
self.age = 0;
setInterval(function growUp() {
// The callback refers to the `self` variable of which
// the value is the expected object.
self.age++;
}, 1000);
}
Như đã đề cập ở trên, arrow function bắt giữ giá trị this
của ngữ cảnh bao quanh gần nhất, vì vậy mã nguồn sau đây hoạt động như mong đợi, ngay cả với arrow function lồng nhau.
function Person() {
this.age = 0;
setInterval(() => {
setTimeout(() => {
this.age++; // `this` properly refers to the person object
}, 1000);
}, 1000);
}
let p = new Person();
Read more about ‘Lexical this’ in arrow functions here
3. Tham số mặc định của hàm
ES6 cho phép bạn thiết lập tham số mặc định trong định nghĩa hàm. Dưới đây là một ví dụ đơn giản.
const getFinalPrice = (price, tax = 0.7) => price + price * tax;
getFinalPrice(500); // 850
4. Toán tử Spread / Rest
Toán tử ...
được gọi là toán tử spread hoặc rest, tùy thuộc vào cách và nơi sử dụng nó.
Khi sử dụng với bất kỳ đối tượng có thể lặp lại nào, nó hoạt động như để “spread” chúng thành các phần tử riêng lẻ:
const makeToast = (breadType, topping1, topping2) => {
return `I had ${breadType} toast with ${topping1} and ${topping2}`;
};
const ingredients = ['wheat', 'butter', 'jam'];
makeToast(...ingredients);
// "I had wheat toast with butter and jam"
makeToast(...['sourdough', 'avocado', 'kale']);
// "I had sourdough toast with avocado and kale"
Spread cũng rất tốt để tạo hình một đối tượng mới từ đối tượng(s) khác:
const defaults = {avatar: 'placeholder.jpg', active: false}
const userData = {username: 'foo', avatar: 'bar.jpg'}
console.log({created: '2017-12-31', ...defaults, ...userData})
// {created: "2017-12-31", avatar: "bar.jpg", active: false, username: "foo"}
Các mảng mới cũng có thể được tạo hình một cách mạch lạc:
const arr1 = [1, 2, 3];
const arr2 = [7, 8, 9];
console.log([...arr1, 4, 5, 6, ...arr2]) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
Một cách sử dụng thông thường khác của ...
là thu thập tất cả các đối số lại thành một mảng. Điều này được gọi là “toán tử rest.”
function foo(...args) {
console.log(args);
}
foo(1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]
5. Mở rộng Đối tượng Chữ
ES6 cho phép khai báo đối tượng chữ bằng cách cung cấp cú pháp tắt để khởi tạo các thuộc tính từ biến và định nghĩa các phương thức hàm. Nó cũng cho phép sử dụng khóa thuộc tính tính toán trong định nghĩa đối tượng chữ.
function getCar(make, model, value) {
return {
// with property value shorthand
// syntax, you can omit the property
// value if key matches variable
// name
make, // same as make: make
model, // same as model: model
value, // same as value: value
// computed values now work with
// object literals
['make' + make]: true,
// Method definition shorthand syntax
// omits `function` keyword & colon
depreciate() {
this.value -= 2500;
}
};
}
let car = getCar('Kia', 'Sorento', 40000);
console.log(car);
// {
// make: 'Kia',
// model:'Sorento',
// value: 40000,
// makeKia: true,
// depreciate: function()
// }
6. Chữ Bát phân và Nhị phân
ES6 hỗ trợ mới cho chữ bát phân và nhị phân. Thêm 0o
hoặc 0O
trước một số sẽ chuyển nó thành giá trị bát phân. Hãy xem mã nguồn dưới đây:
let oValue = 0o10;
console.log(oValue); // 8
let bValue = 0b10; // 0b or 0B for binary
console.log(bValue); // 2
7. Giải nén Mảng và Đối tượng
Giải nén giúp tránh việc cần biến tạm khi làm việc với đối tượng và mảng.
function foo() {
return [1, 2, 3];
}
let arr = foo(); // [1,2,3]
let [a, b, c] = foo();
console.log(a, b, c); // 1 2 3
function getCar() {
return {
make: 'Tesla',
model: 'g95',
metadata: {
vin: '123abc',
miles: '12000'
}
};
}
const {make, model} = getCar();
console.log(make, model); // Tesla g95
const {make, metadata: {miles}} = getCar();
console.log(make, miles); // Tesla 12000
8. super trong Đối tượng
ES6 cho phép sử dụng phương thức super
trong đối tượng (không có lớp) với nguyên mẫu. Dưới đây là một ví dụ đơn giản:
const parent = {
foo() {
console.log("Hello from the Parent");
}
}
const child = {
foo() {
super.foo();
console.log("Hello from the Child");
}
}
Object.setPrototypeOf(child, parent);
child.foo(); // Hello from the Parent
// Hello from the Child
9. Chữ mẫu và Delimiter
ES6 giới thiệu một cách dễ dàng hơn để thêm các phép tương tác được đánh giá tự động.
${ ... }
được sử dụng để hiển thị các biến.-
“` Dấu backtick được sử dụng làm delimiter.
let user = ‘Kevin’;
console.log(Hi ${user}!
); // Hi Kevin!
10. for…of so với for…in
-
for...of
lặp qua các đối tượng có thể lặp lại, chẳng hạn như mảng.const nicknames = [‘di’, ‘boo’, ‘punkeye’];
nicknames.size = 3;
for (let nickname of nicknames) {
console.log(nickname);
}
// di
// boo
// punkeye -
for...in
lặp qua tất cả các thuộc tính có thể đếm được của một đối tượng.const nicknames = [‘di’, ‘boo’, ‘punkeye’];
nicknames.size = 3;
for (let nickname in nicknames) {
console.log(nickname);
}
// 0
// 1
// 2
// size
11. Map và WeakMap
ES6 giới thiệu một bộ dữ liệu mới gọi là Map
và WeakMap
. Hiện nay, chúng ta thực sự sử dụng các bản đồ trong JavaScript suốt thời gian. Trong thực tế, mọi đối tượng đều có thể coi là một Map
.
Một đối tượng được tạo ra từ các khóa (luôn luôn là chuỗi) và giá trị, trong khi trong Map
, bất kỳ giá trị nào (cả đối tượng và giá trị nguyên thủy) đều có thể được sử dụng làm khóa hoặc giá trị. Hãy xem đoạn mã sau:
const myMap = new Map();
const keyString = "a string",
keyObj = {},
keyFunc = () => {};
// setting the values
myMap.set(keyString, "value associated with 'a string'");
myMap.set(keyObj, "value associated with keyObj");
myMap.set(keyFunc, "value associated with keyFunc");
myMap.size; // 3
// getting the values
myMap.get(keyString); // "value associated with 'a string'"
myMap.get(keyObj); // "value associated with keyObj"
myMap.get(keyFunc); // "value associated with keyFunc"
WeakMap
WeakMap
là một Map
trong đó các khóa được tham chiếu yếu, điều này không ngăn các khóa của nó bị thu gom rác. Điều đó có nghĩa bạn không cần phải lo lắng về rò rỉ bộ nhớ.
Một điều khác cần lưu ý ở đây – trong WeakMap
so với Map
, mọi khóa phải là một đối tượng.
WeakMap
chỉ có bốn phương thức delete(key)
, has(key)
, get(key)
và set(key, value)
.
const w = new WeakMap();
w.set('a', 'b');
// Uncaught TypeError: Invalid value used as weak map key
const o1 = {},
o2 = () => {},
o3 = window;
w.set(o1, 37);
w.set(o2, "azerty");
w.set(o3, undefined);
w.get(o3); // undefined, because that is the set value
w.has(o1); // true
w.delete(o1);
w.has(o1); // false
12. Tập hợp và Tập hợp yếu
Đối tượng Set là bộ sưu tập các giá trị duy nhất. Các giá trị trùng lặp sẽ bị bỏ qua, vì bộ sưu tập phải có tất cả các giá trị duy nhất. Các giá trị có thể là các kiểu nguyên thủy hoặc tham chiếu đối tượng.
const mySet = new Set([1, 1, 2, 2, 3, 3]);
mySet.size; // 3
mySet.has(1); // true
mySet.add('strings');
mySet.add({ a: 1, b:2 });
Bạn có thể lặp qua một tập hợp theo thứ tự chèn bằng cách sử dụng phương thức forEach
hoặc vòng lặp for...of
.
mySet.forEach((item) => {
console.log(item);
// 1
// 2
// 3
// 'strings'
// Object { a: 1, b: 2 }
});
for (let value of mySet) {
console.log(value);
// 1
// 2
// 3
// 'strings'
// Object { a: 1, b: 2 }
}
Tập hợp cũng có các phương thức delete()
và clear()
.
Tập hợp yếu
Tương tự như WeakMap
, đối tượng WeakSet
cho phép bạn lưu trữ các đối tượng được giữ yếu trong một bộ sưu tập. Một đối tượng trong WeakSet
chỉ xuất hiện một lần; nó là duy nhất trong bộ sưu tập của WeakSet
.
const ws = new WeakSet();
const obj = {};
const foo = {};
ws.add(window);
ws.add(obj);
ws.has(window); // true
ws.has(foo); // false, foo has not been added to the set
ws.delete(window); // removes window from the set
ws.has(window); // false, window has been removed
13. Lớp trong ES6
ES6 giới thiệu cú pháp lớp mới. Một điều cần lưu ý ở đây là lớp ES6 không phải là một mô hình kế thừa hướng đối tượng mới. Chúng chỉ đơn giản là một cách viết ngắn gọn hơn trên cú pháp dựa trên nguyên mẫu và các hàm tạo mà chúng ta đã sử dụng trong ES5.
Một cách để xem xét lớp trong ES6 là chỉ đơn giản là một cú pháp mới để làm việc với nguyên mẫu và hàm tạo mà chúng ta đã sử dụng trong ES5.
Các hàm được định nghĩa bằng cách sử dụng từ khóa static
triển khai các hàm tĩnh/lớp trên lớp.
class Task {
constructor() {
console.log("task instantiated!");
}
showId() {
console.log(23);
}
static loadAll() {
console.log("Loading all tasks..");
}
}
console.log(typeof Task); // function
const task = new Task(); // "task instantiated!"
task.showId(); // 23
Task.loadAll(); // "Loading all tasks.."
extends và super trong các lớp
Xem xét đoạn mã sau:
class Car {
constructor() {
console.log("Creating a new car");
}
}
class Porsche extends Car {
constructor() {
super();
console.log("Creating Porsche");
}
}
let c = new Porsche();
// Creating a new car
// Creating Porsche
extends
cho phép lớp con kế thừa từ lớp cha trong ES6. Quan trọng là phải gọi super()
trong hàm tạo của lớp con.
Ngoài ra, bạn có thể gọi phương thức của lớp cha trong phương thức của lớp con bằng cách sử dụng super.parentMethodName()
Một số điều cần lưu ý:
- Khai báo lớp không được nâng. Trước tiên, bạn cần phải khai báo lớp của bạn và sau đó truy cập nó, nếu không sẽ ném lỗi ReferenceError.
- Không cần sử dụng từ khóa
function
khi định nghĩa các hàm bên trong định nghĩa lớp.
14. Symbol
Symbol
là một kiểu dữ liệu duy nhất và không thay đổi được giới thiệu trong ES6. Mục đích của một ký hiệu là tạo ra một định danh duy nhất nhưng bạn không bao giờ có thể truy cập đến định danh đó.
Dưới đây là cách bạn tạo một ký hiệu:
const sym = Symbol("some optional description");
console.log(typeof sym); // symbol
Lưu ý rằng bạn không thể sử dụng new
với Symbol(…)
Nếu một ký hiệu được sử dụng làm thuộc tính/khóa của một đối tượng, nó sẽ được lưu trữ một cách đặc biệt để thuộc tính đó sẽ không xuất hiện trong việc liệt kê thông thường của các thuộc tính của đối tượng.
const o = {
val: 10,
[Symbol("random")]: "I'm a symbol",
};
console.log(Object.getOwnPropertyNames(o)); // val
Để lấy các thuộc tính ký hiệu của một đối tượng, sử dụng Object.getOwnPropertySymbols(o)
15. Trình lặp
Một trình lặp truy cập các mục từ một bộ sưu tập một cách lần lượt, đồng thời theo dõi vị trí hiện tại của nó trong chuỗi đó. Nó cung cấp một phương thức next()
trả về mục tiếp theo trong chuỗi. Phương thức này trả về một đối tượng với hai thuộc tính: done và value.
ES6 có Symbol.iterator
mô tả trình lặp mặc định cho một đối tượng. Khi cần lặp qua một đối tượng (như ở đầu của một vòng lặp for..of), phương thức @@iterator của nó được gọi mà không có đối số, và trình lặp được trả về được sử dụng để lấy các giá trị cần lặp qua.
Hãy xem xét một mảng, đó là một đối tượng có thể lặp qua, và trình lặp nó có thể tạo ra để lấy các giá trị của nó:
const arr = [11,12,13];
const itr = arr[Symbol.iterator]();
itr.next(); // { value: 11, done: false }
itr.next(); // { value: 12, done: false }
itr.next(); // { value: 13, done: false }
itr.next(); // { value: undefined, done: true }
Lưu ý rằng bạn có thể viết các trình lặp tùy chỉnh bằng cách định nghĩa obj[Symbol.iterator]()
trong định nghĩa đối tượng.
16. Generators
Hàm tạo là một tính năng mới trong ES6 cho phép một hàm tạo ra nhiều giá trị theo thời gian bằng cách trả về một đối tượng có thể lặp qua để lấy giá trị từ hàm một giá trị tại một thời điểm.
Một hàm tạo trả về một đối tượng có thể lặp qua khi được gọi. Nó được viết bằng cú pháp *
mới cũng như từ khóa yield
mới được giới thiệu trong ES6.
function *infiniteNumbers() {
let n = 1;
while (true) {
yield n++;
}
}
const numbers = infiniteNumbers(); // returns an iterable object
numbers.next(); // { value: 1, done: false }
numbers.next(); // { value: 2, done: false }
numbers.next(); // { value: 3, done: false }
Mỗi lần yield được gọi, giá trị được trả về trở thành giá trị tiếp theo trong chuỗi.
Hãy lưu ý rằng các hàm tạo tính toán các giá trị được trả về theo yêu cầu, cho phép chúng hiệu quả đại diện cho các chuỗi mà tính toán tốn kém hoặc thậm chí là các chuỗi vô hạn.
17. Promises
ES6 có hỗ trợ native cho promises. Một promise là một đối tượng đang chờ một hoạt động bất đồng bộ hoàn thành, và khi hoạt động đó hoàn thành, promise sẽ được thực hiện (resolved) hoặc bị từ chối (rejected).
Cách tiêu chuẩn để tạo một Promise trong JavaScript là bằng cách sử dụng hàm tạo new Promise()
chấp nhận một bộ xử lý (handler) với hai hàm làm tham số. Bộ xử lý đầu tiên (thường được đặt tên là resolve
) là một hàm để gọi khi giá trị tương lai đã sẵn sàng; và bộ xử lý thứ hai (thường được đặt tên là reject
) là một hàm để gọi để từ chối Promise nếu nó không thể giải quyết giá trị tương lai.
const p = new Promise((resolve, reject) => {
if (/* condition */) {
resolve(/* value */); // fulfilled successfully
} else {
reject(/* reason */); // error, rejected
}
});
Mọi Promise đều có một phương thức được đặt tên là then
nhận vào một cặp hàm callback. Hàm callback đầu tiên được gọi nếu Promise được giải quyết, trong khi hàm callback thứ hai được gọi nếu Promise bị từ chối.
p.then((val) => console.log("Promise Resolved", val),
(err) => console.log("Promise Rejected", err));
Trả về một giá trị từ các hàm callback của then
sẽ truyền giá trị đó cho hàm then
tiếp theo.
const hello = new Promise((resolve, reject) => { resolve("Hello") });
hello.then((str) => `${str} World`)
.then((str) => `${str}!`)
.then((str) => console.log(str)) // Hello World!
Khi trả về một Promise, giá trị đã được giải quyết của Promise sẽ được truyền cho hàm callback tiếp theo để hiệu quả kết nối chúng lại với nhau. Đây là một kỹ thuật đơn giản để tránh “callback hell”.
const p = new Promise((resolve, reject) => { resolve(1) });
const eventuallyAdd1 = (val) => new Promise((resolve, reject) => { resolve(val + 1) });
p.then(eventuallyAdd1)
.then(eventuallyAdd1)
.then((val) => console.log(val)); // 3
Ngôn ngữ
- Chinese Version (Thanks to barretlee)
- Portuguese Version (Thanks to alexmoreno)
- Russian Version (Thanks to etnolover)
- Korean Version (Thanks to scarfunk)
- French Version (Thanks to tnga)
- Spanish Version (Thanks to carletex)
- Japanese Version (Thanks to isdh)
Chi tiết Tải xuống:
Tác giả: metagrover
Mã nguồn: https://github.com/metagrover/ES6-for-humans