Các phương thức phải biết
map
Hàm được sử dụng nhiều nhất trong đám, mỗi khi cần thay đổi giá trị phần tử trong array, không thay đổi số lượng phần tử, nghĩ tới map
const numbers = [1, 2, 3, 4]
// cộng thêm một vào tất cả các phần tử
const numbersPlusOne = numbers.map(n => n + 1)
console.log(numbersPlusOne) // [2, 3, 4, 5]
Tạo một array mới, chỉ giữ lại một kiểu property mong muốn trong object
const allActivities = [
{ title: 'My activity', coordinates: [50.123, 3.291] },
{ title: 'Another activity', coordinates: [1.238, 4.292] },
// etc.
]
const allCoordinates = allActivities.map(activity => activity.coordinates)
console.log(allCoordinates) // [[50.123, 3.291], [1.238, 4.292]]
filter
Hàm này sẽ trả về array mới, khi phần tử thỏa điều kiện đặt ra
const numbers = [1, 2, 3, 4, 5, 6]
const oddNumbers = numbers.filter(n => n % 2 !== 0)
console.log(oddNumbers) // [1, 3, 5]
const participants = [
{ id: 'a3f47', username: 'john' },
{ id: 'fek28', username: 'mary' },
{ id: 'n3j44', username: 'sam' },
]
function removeParticipant(participants, id) {
return participants.filter(participant => participant.id !== id)
}
console.log(removeParticipant(participants, 'a3f47'))
// [{ id: 'fek28', username: 'mary' }, { id: 'n3j44', username: 'sam' }];
reduce
Một trong những phương thức khó hiểu nhất, theo quan điểm cá nhân, nhưng một khi đã master rồi thì bạn sẽ làm được khá nhiều thứ hay ho với nó
Về căn bản, reduce
sẽ lấy các giá trị trong array, tính toán các kiểu rồi trả về 1 giá trị. Nó nhận vào các tham số
- Kết quả trả về từ lần tính toán trước, lần chạy đầu tiên, giá trị này là phần tử đầu tiên trong array
- Giá trị phần tử hiện tại trong array
- Giá trị index của phần tử
- Mảng đã gọi trước đó
Hầu như chúng ta chỉ sử dụng 2 tham số đầu
Lấy một ví dụ kinh điển về reduce
, cộng tất cả giá trị trong mảng
const numbers = [37, 12, 28, 4, 9]
const total = numbers.reduce((total, n) => total + n)
console.log(total) // 90
Chúng ta có thể dựng hàm map
và filter
bằng hàm reduce
luôn
const map = (arr, fn) => {
return arr.reduce((mappedArr, element) => {
return [...mappedArr, fn(element)]
}, [])
}
console.log(map([1, 2, 3, 4], n => n + 1)) // [2, 3, 4, 5]
const filter = (arr, fn) => {
return arr.reduce((filteredArr, element) => {
return fn(element) ? [...filteredArr] : [...filteredArr, element]
}, [])
}
console.log(filter([1, 2, 3, 4, 5, 6], n => n % 2 === 0)) // [1, 3, 5]
Giờ xét tới một ví dụ tương đối phức tạp hơn, giảm số chiều trong mảng xuống 1, cụ thể là [1, 2, 3, [4, [[[5, [6, 7]]]], 8]] thành [1, 2, 3, 4, 5, 6, 7, 8]
function flatDeep(arr) {
return arr.reduce((flattenArray, element) => {
return Array.isArray(element)
? [...flattenArray, ...flatDeep(element)]
: [...flattenArray, element]
}, [])
}
console.log(flatDeep([1, 2, 3, [4, [[[5, [6, 7]]]], 8]]))
// [1, 2, 3, 4, 5, 6, 7, 8]
spread operator
Đồng ý đây không phải là một phương thức. Nhưng vì nó quá hữu dụng nên cũng đưa vào luôn
Merge nhiều mảng lại thành 1
const numbers = [1, 2, 3]
const numbersCopy = [...numbers]
console.log(numbersCopy)
// [1, 2, 3]
const otherNumbers = [4, 5, 6]
const numbersConcatenated = [...numbers, ...otherNumbers]
console.log(numbersConcatenated)
// [1, 2, 3, 4, 5, 6]
Lưu ý quan trọng, khi sử dụng spread operator luôn khắc ghi trong lòng là nó sẽ thực hiện một shallow copy, mà shallow copy nghĩa là gì, là nó sẽ copy dùng cách đỡ tốn công nhất có thể, nếu các giá trị trong mảng kiểu số, chữ (primitive types) thì không vấn đề, khi trong mảng đó chứa mảng khác, object thì nó chỉ trỏ tới cùng đối tượng gốc thôi, chứ không phải là sao i
const arr = ['foo', 42, { name: 'Thomas' }]
let copy = [...arr]
copy[0] = 'bar'
console.log(arr)
// No mutations: ["foo", 42, { name: "Thomas" }]
console.log(copy)
// ["bar", 42, { name: "Thomas" }]
copy[2].name = 'Hello'
console.log(arr)
// /!\ MUTATION ["foo", 42, { name: "Hello" }]
console.log(copy)
// ["bar", 42, { name: "Hello" }]
Phải nhắc lại, vì đây là trường hợp hay bị bug nhất. Nên khi cần giải quyết vấn đề copy này triệt để, nhớ đến cloneDeep của Lodash
Biết thì tốt
includes
Nếu đã từng sử dụng phương thức indexOf
để kiểm tra xem phần tử đó có tồn tại trong array không, thì bạn có thể thay thế bằng việc dùng includes
const sports = ['football', 'archery', 'judo']
const hasFootball = sports.includes('football')
console.log(hasFootball) // true
concat
Phương thức merge nhiều mảng thành 1
const numbers = [1, 2, 3]
const otherNumbers = [4, 5, 6]
const numbersConcatenated = numbers.concat(otherNumbers)
console.log(numbersConcatenated)
// [1, 2, 3, 4, 5, 6]
// You can merge as many arrays as you want
function concatAll(arr, ...arrays) {
return arr.concat(...arrays)
}
console.log(concatAll([1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]))
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
Loop qua mảng, chậm hơn for
nhưng xài tiện hơn
const numbers = [1, 2, 3, 4, 5]
numbers.forEach(console.log)
// 1 0 [ 1, 2, 3 ]
// 2 1 [ 1, 2, 3 ]
// 3 2 [ 1, 2, 3 ]
indexOf
Được sử dụng thường xuyên để kiểm tra phần tử có tồn tại trong mảng không
const sports = ['football', 'archery', 'judo']
const judoIndex = sports.indexOf('judo')
console.log(judoIndex)
// 2
find
Khá tương đồng với hàm filter
, chúng ta cung cấp cho nó một hàm để kiểm tra tất cả các phần tử của mảng. Tuy nhiên nó sẽ trả về phần tử đầu tiên thỏa điều kiện chứ không chạy hết toàn bộ mảng.
const users = [
{ id: 'af35', name: 'john' },
{ id: '6gbe', name: 'mary' },
{ id: '932j', name: 'gary' },
]
const user = users.find(user => user.id === '6gbe')
console.log(user)
// { id: '6gbe', name: 'mary' }
findIndex
Giống như hàm find, nhưng thay vì trả về phần tử, nó trả về index của phần tử
const users = [
{ id: 'af35', name: 'john' },
{ id: '6gbe', name: 'mary' },
{ id: '932j', name: 'gary' },
]
const user = users.findIndex(user => user.id === '6gbe')
console.log(user) // 1
slice
Khi chúng ta cần lấy một đoạn trong mảng, hoặc copy một đoạn, chúng ta nhớ tới slice
. Nó cũng thực hiện một shallow copy
const numbers = [1, 2, 3, 4, 5]
const copy = numbers.slice()
Ví dụ, chúng ta muốn lấy một số đoạn chat messages từ API, 2 cách làm với vòng lặp for
và slice
// The "traditional way" to do it:
// xác định số lượng muốn lấy, sử dụng vòng lặp for
const nbMessages = messages.length < 5 ? messages.length : 5
let messagesToShow = []
for (let i = 0; i < nbMessages; i++) {
messagesToShow.push(posts[i])
}
// Nếu "arr" ít hơn 5 phần tử,
// nó vẫn chạy bình thường
const messagesToShow = messages.slice(0, 5)
some
Để kiểm tra có ít nhất một phần tử trong mảng thỏa điều kiện
const users = [
{
id: 'fe34',
permissions: ['read', 'write'],
},
{
id: 'a198',
permissions: [],
},
{
id: '18aa',
permissions: ['delete', 'read', 'write'],
},
]
const hasDeletePermission = users.some(user =>
user.permissions.includes('delete')
)
console.log(hasDeletePermission) // true
every
Tất cả các phần tử trong mảng điều thỏa điều kiện
const users = [
{
id: 'fe34',
permissions: ['read', 'write'],
},
{
id: 'a198',
permissions: [],
},
{
id: '18aa',
permissions: ['delete', 'read', 'write'],
},
]
const hasAllReadPermission = users.every(user =>
user.permissions.includes('read')
)
console.log(hasAllReadPermission) // false
from
Tạo một array mới từ một object hoặc một đối tượng bất kỳ có thể tạo được
const nodes = document.querySelectorAll('.todo-item')
//lấy danh sách NodeList
const todoItems = Array.from(nodes)
// có thể sử dụng các phương thức của array trên todoItems này
todoItems.forEach(item => {
item.addEventListener('click', function() {
alert(`You clicked on ${item.innerHTML}`)
})
})