Các tính năng ES2015 (ES6) (part 2/2)
Bài đăng này đã không được cập nhật trong 5 năm
Part 1: Click here
11. Các method mới cho String
Các giá trị string có thêm một vài instance method mới:
repeat()
codePointAt()
repeat()
Lặp lại chuỗi theo số lần chỉ định:
'Ho'.repeat(3) // 'HoHoHo'
repeat()
sẽ trả về một chuỗi rỗng nếu không có tham số truyền vào, hoặc tham số là 0
. Nếu tham số truyền vào là âm thì sẽ gặp lỗi RangeError.
codePointAt()
Method này có thể được sử dụng để xử lý những ký tự mà không thể biểu thị bằng 1 đơn vị Unicode 16-bit.
Nếu sự dụng charCodeAt()
bạn sẽ phải cần lấy từng phần của ký tự Unicode rồi gộp chúng lại với nhau. Sử dụng codePointAt()
bạn sẽ có thể lấy được toàn bộ chỉ bằng một lần gọi.
Ví dụ như ký tự Trung Quốc “𠮷” được tạo bởi hai phần UTF-16:
"𠮷".charCodeAt(0).toString(16) // d842
"𠮷".charCodeAt(1).toString(16) // dfb7
Hai ký tự unicode lấy được ghép vào sẽ được ký tự Trung Quốc ban đầu:
"\u{d842}\u{dfb7}" // "𠮷"
Sự dụng codePointAt()
bạn sẽ có được kết quả tương tự:
"𠮷".codePointAt(0).toString(16) // 20bb7
"\u{20bb7}" // "𠮷"
12. Các method mới cho Object
ES2015 đem tới một vài static method cho Object:
Object.is()
xác định xem hai giá trị có bằng nhau khôngObject.assign()
được dùng để shallow copy một objectObject.setPrototypeOf
dùng để đặt prototype cho một object
Object.is()
Method này có mục đích giúp bạn so sánh các giá trị.
Cách sử dụng
Object.is(a,b)
Kết quả luôn là false
trừ khi:
a
vàb
cùng là một objecta
vàb
là các string bằng nhau (string bằng nhau khi chúng được tạo bởi các ký tự giống nhau)a
vàb
là các số bằng nhaua
vàb
cùng làundefined
, cùng lànull
, cùng làNaN
, cùng làtrue
hoặc cùng làfalse
Các bạn lưu ý rằng trong JavaScript 0
và -0
là hai giá trị khác nhau.
Object.assign()
Method này copy tất cả các thuộc tính enumerable
của một hoặc nhiều object vào một object khác.
Nó chủ yếu được sử dụng để tạo ra một shallow copy của một object.
const copied = Object.assign({}, original)
Vì shallow copy nên chỉ có object "chính" được copy còn các object "con" sẽ chỉ copy tham chiếu tới nó.
Nếu bạn sửa một thuộc tính trong các object được tham chiếu tới thì thay đổi đó cũng thấy được trong object được copy ra.
const original = {
name: 'Fiesta',
car: {
color: 'blue'
}
}
const copied = Object.assign({}, original)
original.name = 'Focus'
original.car.color = 'yellow'
copied.name // Fiesta
copied.car.color // yellow
Copy từ nhiều object:
const wisePerson = {
isWise: true
}
const foolishPerson = {
isFoolish: true
}
const wiseAndFoolishPerson = Object.assign({}, wisePerson, foolishPerson)
console.log(wiseAndFoolishPerson) // { isWise: true, isFoolish: true }
Object.setPrototypeOf()
Method này đặt prototype cho một object. Nó nhận hai đối số: object và prototype.
Cách dùng:
Object.setPrototypeOf(object, prototype)
Ví dụ:
const animal = {
isAnimal: true
}
const mammal = {
isMammal: true
}
mammal.__proto__ = animal
mammal.isAnimal // true
const dog = Object.create(animal)
dog.isAnimal //true
console.log(dog.isMammal) // undefined
Object.setPrototypeOf(dog, mammal)
dog.isAnimal // true
dog.isMammal // true
13. Toán tử spread
Bạn có thể mở rộng một mảng, một object hoặc một string bằng toán tử spread: ...
Ví dụ, cho một mảng như sau:
const a = [1, 2, 3]
bạn có thể tạo một mảng mới mở rộng từ mảng cũ như sau:
const b = [...a, 4, 5, 6]
b // [1, 2, 3, 4, 5, 6]
Bạn cũng có thể copy một mảng bằng cách:
const c = [...a]
Cũng có thể sử dụng tương tự với object. Sao chép một object:
const newObj = { ...oldObj }
Khi sử dụng với string, toán tử spread sẽ tạo ra một mảng với các phần tử là các ký tự của string:
const hey = 'hey'
const arrayized = [...hey] // ['h', 'e', 'y']
Toán tử spread có những ứng dụng vô cùng hữu ích. Ứng dụng quan trọng nhất là khả năng dùng mảng làm đối số một cách rất đơn giản:
const f = (foo, bar) => console.log(foo + bar)
const a = [1, 2]
// viết
f(...a)
// sẽ tương đương với viết
f(1, 2)
(Trước đây bạn có thể viết f.apply(null, a)
, tuy nhiên cách viết này không được gọn và khá khó đọc.)
Toán tử spread cũng hữu dụng trong việc array destruturing:
const numbers = [1, 2, 3, 4, 5]
[first, second, ...others] = numbers
first // 1
second // 2
others // [3, 4, 5]
(phần tử others trong ví dụ này được gọi là rest element)
Từ ES2018 ta có thể sử dụng toán tử spread với object tương tự như ví dụ trên, và others ở đây được gọi là rest properties:
const { first, second, ...others } = {
first: 1,
second: 2,
third: 3,
fourth: 4,
fifth: 5
}
first // 1
second // 2
others // { third: 3, fourth: 4, fifth: 5 }
14. Map
Một cấu trúc dữ liệu dạng Map cho phép chúng ta liên kết dữ liệu với một key.
Trước khi có Map, người ta thường sử dụng object thay vì nó:
const car = {}
car['color'] = 'red'
car.owner = 'Flavio'
car['color'] //red
car.color //red
car.owner //Flavio
car['owner'] //Flavio
ES6 mang tới cho chúng ta cấu trúc dữ liệu Map, một công cụ thích hợp hơn để xử lý việc tổ chức dữ liệu như thế này.
Khởi tạo Map
Một Map có thể khởi tạo bằng cách gọi:
const m = new Map()
Thêm item vào Map
Bạn có thể thêm item vào map bằng method set
:
m.set('color', 'red')
m.set('age', 2)
Lấy item từ map
Và bạn có thể lấy item từ map bằng get
:
const color = m.get('color')
const age = m.get('age')
Xóa item khỏi map
Dùng method delete()
:
m.delete('color')
Xóa toàn bộ item khỏi map
Dùng method clear()
:
m.clear()
Kiểm tra xem item có trong map không
Dùng method has()
:
const hasColor = m.has('color')
Kiểm tra số item trong map
Dùng thuộc tính size
:
const size = m.size
Khởi tạo map cùng với giá trị
Bạn có thể khởi tạo map với nhiều giá trị:
const m = new Map([['color', 'red'], ['owner', 'Flavio'], ['age', 2]])
Map key
Bất kì kiểu giá trị nào (object, array, string, number) đều có thể dùng làm giá trị của item trong map, và bất kì kiểu giá trị nào cũng có thể dùng làm key của map kể cả object.
Nếu bạn dùng get()
để lấy một key không có trong map, thì get() sẽ trả về undefined
.
const m = new Map()
m.set(NaN, 'test')
m.get(NaN) // test
m.set(+0, 'test')
m.get(-0) // test
Lặp các key trong Map
Map cung cấp cho chúng ta method keys
và chúng ta có thể dùng nó để lặp tất cả các key trong map.
for (const k of m.keys()) {
console.log(k)
}
Lặp các giá trị trong Map
Map cung cấp cho chúng ta method values
và chúng ta có thể dùng nó để lặp tất cả các giá trị trong map.
for (const k of m.values()) {
console.log(k)
}
Lặp các cặp key, value trong Map
Map cung cấp cho chúng ta method entries
và chúng ta có thể dùng nó để lặp tất cả các key trong map.
for (const k of m.entries()) {
console.log(k)
}
có thể viết lược giản đi thành:
for (const [k, v] of m) {
console.log(k, v)
}
Convert key/value của map thành mảng
Ta có thể dùng toán tử spread để làm điều này:
const ka = [...m.keys()]
const va = [...m.values()]
WeakMap
WeakMap là một loại Map đặc biệt. Nó là một tập hợp các cặp key/value mà các key phải là object còn value thì có thể là các giá trị tùy ý.
Trong một Map, các item không bao giờ bị tự động giải phóng khỏi bộ nhớ. Còn trong WeakMap thì tất cả các item của nó đều có thể bị giải phóng dễ dàng. Mỗi key trong WeakMap là một object. Khi tham chiếu tới những object này bị mất đi thì các giá trị của chúng có thể bị giải phóng khỏi bộ nhớ. Do đó nó thiếu nhiều tính năng so với Map:
- bạn không thể lặp WeakMap
- bạn không thể đồng thời xóa toàn bộ item khỏi WeakMap
- bạn không thể kiếm tra size của nó
WeakMap sử dụng được các method sau:
- get(k)
- set(k, v)
- has(k)
- delete(k)
Các trường hợp có thể sử dụng WeakMap khá là khó xác định so với Map, và bạn có thể sẽ không bao giờ cần phải dùng đến nó, nhưng về bản chất thì nó có thể dùng đề build memory-sensitive cache mà không can thiệp tới việc giải phóng memory hoặc dùng trong việc encapsulation và ẩn thông tin.
15. Set
Cấu trúc dữ liệu Set cho phép chúng ta thêm dữ liệu vào một container.
Một Set là một tập hợp các object hoặc các dữ liệu kiểu nguyên thủy (string, number hoặc boolean), bạn có thể coi nó như một Map mà các giá trị thêm vào được sử dụng làm key, và giá trị của các key này luôn là giá trị boolean true.
Khởi tạo một Set
Một Set được khởi tạo bằng cách gọi:
const s = new Set()
Thêm item vào Set
Bạn có thể thêm item vào Set bằng method add
:
s.add('one')
s.add('two')
Một set sẽ chỉ lưu trữ các phần tử unique, nên gọi s.add('one')
nhiều lần sẽ không thêm dữ liệu mới vào set.
Bạn không thể cùng lúc thêm nhiều phần tử vào một set mà phải gọi add()
nhiều lần.
Kiểm tra xem item có trong set không
s.has('one') // true
s.has('three') // false
Xóa item khỏi Set
Dùng method delete()
:
s.delete('one')
Xác định số item trong một Set
Dùng thuộc tính size
:
s.size
Xóa tất cả item khỏi Set
Dùng method clear()
:
s.clear()
Lặp các item trong một Set
Dùng method keys()
hoặc values()
- hai method này tương đương nhau:
for (const k of s.keys()) {
console.log(k)
}
for (const k of s.values()) {
console.log(k)
}
Method entries()
sẽ trả về một bộ lặp mà bạn có thể sử dụng như sau:
const i = s.entries()
console.log(i.next())
mỗi lần gọi i.next()
sẽ trả về từng phần tử dưới dạng một object { value, done = false }
cho tới khi vòng lặp kết thúc, lúc đó done
sẽ là true
.
Bạn cũng có thể dùng method forEach() với set:
s.forEach(v => console.log(v))
hoặc bạn có thể chỉ cần dùng duy nhất vòng lặp for..of
:
for (const k of s) {
console.log(k)
}
Khởi tạo Set cùng với giá trị
Bạn có thể khởi tạo một Set với nhiều giá trị:
const s = new Set([1, 2, 3, 4])
Chuyển các key của Set thành một mảng
Chúng ta có thể sử dụng toán tử spread để làm việc này:
const a = [...s.keys()]
// or
const a = [...s.values()]
WeakSet
WeakSet là một loại Set đặc biệt. Nó là một tập hợp chỉ các object. Các object trong nó cũng là unique.
Trong một Set, các item không bao giờ bị tự động giải phóng khỏi bộ nhớ. Còn trong WeakSet thì tất cả các item của nó đều có thể bị giải phóng dễ dàng. Mỗi key trong WeakSet là một object. Khi tham chiếu tới những object này bị mất đi thì các giá trị của chúng có thể bị giải phóng khỏi bộ nhớ. Do đó nó thiếu nhiều tính năng so với Set:
- bạn không thể lặp WeakSet
- bạn không thể đồng thời xóa toàn bộ item khỏi WeakSet
- bạn không thể kiếm tra size của nó
WeakSet thường chỉ sử dụng trong code ở cấp độ framework và chỉ sử dụng được các method sau:
- add()
- has()
- delete()
16. Generators
Generators là một kiểu function đặc biệt với khả năng tạm ngừng chính mình và hoạt đông tiếp sau đó. Khi nó tạm ngừng để đợi, các dòng code khác trong dãy chờ sẽ chạy còn nó thì vẫn có quyền tiếp tục hoạt động khi "thứ nó đợi" hoàn thành.
Tất cả những việc trên có thể được thực hiện một cách đơn giản chỉ bằng một keyword duy nhất: yield
. Khi một generator có chứa keyword này thì việc thực thi của nó sẽ có thể được tạm dừng lại.
Một generator có thể chứa nhiều keyword yield
, do đó có thể tạm dừng chính nó nhiều lần. Generator được chỉ định bằng keyword *function
.
Generator đã mở ra các mô hình lập trình hoàn toàn mới trong JavaScript, cho phép:
- Trao đổi 2 chiều khi một generator đang chạy
- Vòng lặp while có thể tồn tại rất lâu mà không làm cho chương trình của bạn bị treo
Dưới đây là một ví dụ về generator thể hiện cách nó vận hành như thế nào:
function *calculator(input) {
var doubleThat = 2 * (yield (input / 2))
var another = yield (doubleThat)
return (input * doubleThat * another)
}
Chúng ta khởi tạo nó với:
const calc = calculator(10)
Sau đó chúng ta bắt đầu bộ lặp với generator của chúng ta:
calc.next()
Vòng lặp đầu tiên sẽ khởi động bộ lặp. Code sẽ return cho chúng ta object:
{
done: false
value: 5
}
Giải thích: function được chạy với tham số truyền vào input = 10
. Nó chạy cho tới khi đến keyword yield
đầu tiên và trả về content của yield
: input / 2
= 5
. Vậy là chúng ta có được giá trị là 5 và status của việc lặp là chưa xong (vì function chỉ đang tạm dừng).
Trong vòng lặp thứ 2, chúng ta truyền vào giá trị 7
:
calc.next(7)
và chúng ta nhận lại được:
{
done: false
value: 14
}
7
đã được đưa vào phần giá trị của biến doubleThat
. Lúc này input / 2
sẽ thay bằng 7
và doubleThat
= 2 * 7.
Tiếp theo chúng ta tới keyword yield
thứ hai và nó trả về doubleThat
dó đó giá trị trả về là 14
.
Trong vòng lặp tiếp theo và cũng là cuối cùng, chúng ta truyền vào 100
.
calc.next(100)
Lúc này doubleThat
được thay bằng 100
do đó var another
= 100
và chúng ta nhận được:
{
done: true
value: 14000
}
Bởi vì lúc này đã chạy hết các vòng lặp (không còn keyword yield nào nữa) và trong function chúng ta return (input * doubleThat * another)
tương đương với 10 * 14 *100
= 14000
.
Vậy là mình đã giới thiệu hết các feature trong ES2015. Các bạn có thể đọc tiếp về các feature trong ES2016 ở bài viết tiếp theo.
All rights reserved