JavaScript: Tìm Hiểu Về Hàm Hủy (destructuring), Tham Số Rest Và Cú ...

Giới thiệu

Nhiều tính năng mới để làm việc với mảng và đối tượng đã được cung cấp cho ngôn ngữ JavaScript kể từ Phiên bản 2015 của đặc tả ECMAScript. Một vài trong số những điều đáng chú ý mà bạn sẽ tìm hiểu trong bài viết này là cấu trúc hàm hủy, tham số rest và cú pháp spread. Các tính năng này cung cấp nhiều cách truy cập trực tiếp hơn vào các thành phần của một mảng hoặc một đối tượng và có thể làm cho việc làm việc với các cấu trúc dữ liệu này nhanh hơn và ngắn gọn hơn.

Nhiều ngôn ngữ khác không có cú pháp tương ứng cho cấu trúc hủy, tham số rest và cú pháp spread, vì vậy các tính năng này có thể có một đường cong học tập cho cả các nhà phát triển JavaScript mới và những người đến từ ngôn ngữ khác. Trong bài viết này, bạn sẽ học cách hủy cấu trúc các đối tượng và mảng, cách sử dụng toán tử spread để giải nén các đối tượng và mảng cũng như cách sử dụng tham số rest trong lời gọi hàm.

Hàm hủy (Destructuring)

Cấu trúc hủy là một cú pháp cho phép bạn gán các thuộc tính đối tượng hoặc các mục mảng dưới dạng các biến. Điều này có thể làm giảm đáng kể các dòng mã cần thiết để thao tác dữ liệu trong các cấu trúc này. Có hai kiểu cấu trúc hủy: Cấu trúc hủy đối tượng và cấu trúc hủy mảng.

Cấu trúc hủy đối tượng

Cấu trúc hủy đối tượng cho phép bạn tạo các biến mới bằng cách sử dụng một thuộc tính đối tượng làm giá trị.

Hãy xem xét ví dụ này, một đối tượng đại diện cho một note với một id, title và date:

const note = { id: 1, title: 'My first note', date: '01/01/1970', }

Theo cách thức truyền thống, nếu bạn muốn tạo một biến mới cho từng thuộc tính, bạn sẽ phải chỉ định từng biến riêng lẻ, với rất nhiều lần lặp lại:

// Tạo các biến từ các thuộc tính của đối tượng const id = note.id const title = note.title const date = note.date

Với cấu trúc hủy đối tượng, tất cả điều này có thể được thực hiện trong một dòng. Bằng cách đặt các biến trong cặp ngoặc xoắn {}, JavaScript sẽ tạo các biến mới từ mỗi thuộc tính có cùng tên:

const { id, title, date } = note

Bây giờ, ta hãy console.log() các biến mới:

console.log(id) console.log(title) console.log(date)

Bạn sẽ nhận được các giá trị thuộc tính ban đầu dưới dạng đầu ra:

Output

1 My first note 01/01/1970

Lưu ý: Cấu trúc hủy đối tượng không sửa đổi đối tượng ban đầu. Bạn vẫn có thể gọi note với tất cả các mục còn nguyên vẹn.

Phép gán mặc định cho cấu trúc hủy đối tượng tạo ra các biến mới có cùng tên với thuộc tính đối tượng. Nếu bạn không muốn biến mới có cùng tên với tên thuộc tính, bạn cũng có tùy chọn đổi tên biến mới bằng cách sử dụng dấu hai chấm (:) để quyết định một tên mới, như noteId trong phần sau:

const { id: noteId, title, date } = note

Ghi biến mới noteId vào console:

console.log(noteId)

Bạn sẽ nhận được kết quả sau:

Output

1

Bạn cũng có thể hủy cấu trúc các giá trị đối tượng lồng nhau. Ví dụ: cập nhật đối tượng note để có một đối tượng author lồng nhau:

const note = { id: 1, title: 'My first note', date: '01/01/1970', author: { firstName: 'Sherlock', lastName: 'Holmes', }, }

Bây giờ bạn có thể hủy cấu trúc note, sau đó hủy cấu trúc lại một lần nữa để tạo các biến từ các thuộc tính author:

// Hủy các thuộc tính lồng nhau const { id, title, date, author: { firstName, lastName }, } = note

Tiếp theo, ghi nhật ký các biến mới firstName và lastName sử dụng các ký tự mẫu:

console.log(`${firstName}${lastName}`)

Điều này sẽ cho kết quả sau:

Output

Sherlock Holmes

Lưu ý rằng trong ví dụ này, mặc dù bạn có quyền truy cập vào nội dung của đối tượng author, nhưng bản thân đối tượng author không thể truy cập được. Để truy cập một đối tượng cũng như các giá trị lồng nhau của nó, bạn sẽ phải khai báo chúng một cách riêng biệt:

// Truy cập các đối tượng và giá trị lồng nhau const { author, author: { firstName, lastName }, } = note console.log(author)

Đoạn mã này sẽ xuất ra đối tượng author:

Output

{firstName: "Sherlock", lastName: "Holmes"}

Việc hủy cấu trúc một đối tượng không chỉ hữu ích để giảm số lượng mã mà bạn phải viết; nó cũng cho phép bạn nhắm mục tiêu quyền truy cập của mình vào các thuộc tính mà bạn quan tâm.

Cuối cùng, cấu trúc hủy có thể được sử dụng để truy cập các thuộc tính đối tượng của các giá trị nguyên thủy. Ví dụ: String là một đối tượng toàn cục cho chuỗi và có thuộc tính length:

const { length } = 'A string'

Thao tác này sẽ tìm thuộc tính độ dài vốn có của một chuỗi và đặt nó bằng biến length. Truy cập length để xem điều này có hiệu quả không:

console.log(length)

Bạn sẽ nhận được kết quả sau:

Output

8

Chuỗi A string đã được chuyển đổi hoàn toàn thành một đối tượng ở đây để truy xuất thuộc tính length.

Cấu trúc mảng

Cấu trúc mảng cho phép bạn tạo các biến mới bằng cách sử dụng một mục mảng làm giá trị. Hãy xem xét ví dụ này, một mảng với các phần khác nhau của ngày:

const date = ['1970', '12', '01']

Mảng trong JavaScript được đảm bảo duy trì thứ tự của chúng, vì vậy trong trường hợp này, chỉ mục đầu tiên sẽ luôn là năm, chỉ mục thứ hai sẽ là tháng, v.v. Biết được điều này, bạn có thể tạo các biến từ các mục trong mảng:

// Tạo các biến từ các phần tử mảng const year = date[0] const month = date[1] const day = date[2]

Nhưng làm điều này theo cách thủ công có thể chiếm nhiều dung lượng trong mã của bạn. Với cấu trúc hủy mảng, bạn có thể giải nén các giá trị từ mảng theo thứ tự và gán chúng cho các biến riêng của chúng, như sau:

// Đưa các giá trị của cấu trúc mảng huy vào các biến const [year, month, day] = date

Bây giờ ghi lại các biến mới:

console.log(year) console.log(month) console.log(day)

Bạn sẽ nhận được kết quả sau:

Output

1970

12

01

Có thể bỏ qua các giá trị bằng cách để trống cú pháp cấu trúc giữa các dấu phẩy:

// Bỏ qua phần tử thứ hai của mảng const [year, , day] = date console.log(year) console.log(day)

Chạy điều này sẽ cung cấp giá trị của year và day:

Output

1970

01

Các mảng lồng nhau cũng có thể hủy. Đầu tiên, tạo một mảng lồng nhau:

// Tạo một mảng lồng const nestedArray = [1, 2, [3, 4], 5]

Sau đó, rút gọn cấu trúc mảng đó và lưu vào các biến mới:

// Rút gọn các mục lồng nhau const [one, two, [three, four], five] = nestedArray console.log(one, two, three, four, five)

Bạn sẽ nhận được kết quả sau:

Output

1 2 3 4 5

Cú pháp rút gọn có thể được áp dụng để rút gọn các tham số trong một hàm. Để kiểm tra điều này, bạn sẽ rút gọn keys và values ra ngoài Object.entries().

Đầu tiên, khai báo đối tượng note:

const note = { id: 1, title: 'My first note', date: '01/01/1970', }

Với đối tượng này, bạn có thể liệt kê các cặp khóa-giá trị bằng cách rút gọn các đối số khi chúng được truyền cho phương thức forEach():

// Sử dụng forEach Object.entries(note).forEach(([key, value]) => { console.log(`${key}: ${value}`) })

Hoặc bạn có thể thực hiện điều tương tự bằng cách sử dụng một vòng lặp for:

// Sử dụng vòng lặp for for (let [key, value] of Object.entries(note)) { console.log(`${key}: ${value}`) }

Dù bằng cách nào, bạn sẽ nhận được những điều sau:

Output

id: 1 title: My first note date: 01/01/1970

Cấu trúc rút gọn đối tượng và rút gọn mảng có thể được kết hợp trong một nhiệm vụ rút gọn duy nhất. Các tham số mặc định cũng có thể được sử dụng với cấu trúc rút gọn, như trong ví dụ này đặt ngày mặc định thành new Date().

Đầu tiên, khai báo đối tượng note:

const note = { title: 'My first note', author: { firstName: 'Sherlock', lastName: 'Holmes', }, tags: ['personal', 'writing', 'investigations'], }

Sau đó, hủy cấu trúc đối tượng, đồng thời thiết lập một biến date mới với giá trị mặc định là new Date():

const { title, date = new Date(), author: { firstName }, tags: [personalTag, writingTag], } = note console.log(date)

console.log(date) sau đó sẽ đưa ra kết quả tương tự như sau:

Output

Fri May 08 2020 23:53:49 GMT-0500 (Central Daylight Time)

Như được trình bày trong phần này, cú pháp gán hàm hủy cấu trúc bổ sung nhiều tính linh hoạt cho JavaScript và cho phép bạn viết mã ngắn gọn hơn. Trong phần tiếp theo, bạn sẽ thấy cách sử dụng cú pháp spread để mở rộng cấu trúc dữ liệu vào các mục dữ liệu cấu thành của chúng.

Spread

Cú pháp Spread ( ...) là một bổ sung hữu ích khác cho JavaScript để làm việc với mảng, đối tượng và lời gọi hàm. Spread cho phép các đối tượng và các tệp lặp (chẳng hạn như mảng) được giải nén hoặc mở rộng, có thể được sử dụng để tạo các bản sao đơn giản của cấu trúc dữ liệu nhằm tăng tính dễ thao tác dữ liệu.

Spread với Mảng

Spread có thể đơn giản hóa các tác vụ thông thường với mảng. Ví dụ: giả sử bạn có hai mảng và muốn kết hợp chúng:

// Tạo một mảng const tools = ['hammer', 'screwdriver'] const otherTools = ['wrench', 'saw']

Ban đầu bạn sẽ sử dụng concat() để nối hai mảng:

// Ghép nối tools và otherTools với nhau const allTools = tools.concat(otherTools) Sao chép

Bây giờ bạn cũng có thể sử dụng spread để giải nén các mảng thành một mảng mới:

// Giải nén mảng tools thàng mảng allTools const allTools = [...tools, ...otherTools] console.log(allTools)

Kết quả:

Output

["hammer", "screwdriver", "wrench", "saw"]

Điều này có thể đặc biệt hữu ích với tính bất biến. Ví dụ: bạn có thể đang làm việc với một ứng dụng mà trong đó các users được lưu trữ trong một mảng các đối tượng:

// Mảng users const users = [ { id: 1, name: 'Ben' }, { id: 2, name: 'Leslie' }, ]

Bạn có thể sử dụng push để sửa đổi mảng hiện có và thêm người dùng mới, đây sẽ là tùy chọn có thể thay đổi:

// Thêm một user mới const newUser = { id: 3, name: 'Ron' } users.push(newUser) Sao chép

Nhưng điều này làm thay đổi mảng users mà chúng ta có thể muốn giữ nguyên.

Spread cho phép bạn tạo một mảng mới từ mảng hiện có và thêm một item mới vào cuối:

const updatedUsers = [...users, newUser] console.log(users) console.log(updatedUsers)

Bây giờ mảng mới updatedUsers sẽ có người dùng mới, nhưng mảng users ban đầu vẫn không thay đổi:

Output

[{id: 1, name: "Ben"} {id: 2, name: "Leslie"}] [{id: 1, name: "Ben"} {id: 2, name: "Leslie"} {id: 3, name: "Ron"}]

Tạo bản sao dữ liệu thay vì thay đổi dữ liệu hiện có có thể giúp ngăn chặn những thay đổi không mong muốn. Trong JavaScript, khi bạn tạo một đối tượng hoặc mảng và gán nó cho một biến khác, bạn không thực sự tạo một đối tượng mới — bạn đang truyền một tham chiếu.

Lấy ví dụ này, trong đó một mảng được tạo và gán cho một biến khác:

// Tạo một mảng const originalArray = ['one', 'two', 'three'] // Gán mảng cho một biến khác const secondArray = originalArray

Xóa mục cuối cùng của Mảng thứ hai sẽ làm thay đổi mục đầu tiên:

// Xóa item cuối của mảng thứ hai secondArray.pop() console.log(originalArray) Sao chép

Điều này sẽ cho kết quả:

Output

["one", "two"]

Spread cho phép bạn tạo bản sao nông (shallow) của một mảng hoặc đối tượng, có nghĩa là bất kỳ thuộc tính cấp cao nhất nào cũng sẽ được sao chép, nhưng các đối tượng lồng nhau sẽ vẫn được chuyển qua tham chiếu. Đối với các mảng hoặc đối tượng đơn giản, một bản sao nông có thể là tất cả những gì bạn cần.

Nếu bạn viết cùng một mã ví dụ nhưng sao chép mảng với spread, thì mảng ban đầu sẽ không còn được sửa đổi nữa:

// Tạo một mảng const originalArray = ['one', 'two', 'three'] // Dùng spread để tạo một bản copy nông (shallow) const secondArray = [...originalArray] // Xóa phần tử cuối của mảng secondArray secondArray.pop() console.log(originalArray)

Những điều sau sẽ được ghi vào bảng điều khiển:

Output

["one", "two", "three"]

Spread cũng có thể được sử dụng để chuyển đổi một Set hoặc bất kỳ iterable nào thành mảng.

Tạo một set mới và thêm một số item vào nó:

// Tạo một set const set = new Set() set.add('octopus') set.add('starfish') set.add('whale') Sao chép

Tiếp theo, sử dụng toán tử spread với set và log kết quả:

// Chuyển Set thành Array const seaCreatures = [...set] console.log(seaCreatures)

Kết quả:

Output

["octopus", "starfish", "whale"]

Điều này cũng có thể hữu ích để tạo một mảng từ một chuỗi:

const string = 'hello' const stringArray = [...string] console.log(stringArray)

Kết quả:

Output

["h", "e", "l", "l", "o"]

Spread với các đối tượng

Khi làm việc với các đối tượng, spread có thể được sử dụng để sao chép và cập nhật các đối tượng.

Ban đầu, Object.assign() được sử dụng để sao chép một đối tượng:

// Tạo một Object và một bản copy Object bằng Object.assign() const originalObject = { enabled: true, darkMode: false } const secondObject = Object.assign({}, originalObject)

secondObject bây giờ sẽ có một bản sao của originalObject.

Điều này được đơn giản hóa với cú pháp spread — bạn có thể sao chép nông một đối tượng bằng cách spread đối tượng đó thành một đối tượng mới:

// Tạo một đối tượng và bản sao của nó bằng spread const originalObject = { enabled: true, darkMode: false } const secondObject = { ...originalObject } console.log(secondObject)

Kết quả:

Output

{enabled: true, darkMode: false}

Cũng giống như với mảng, điều này sẽ chỉ tạo ra một bản sao nông và các đối tượng lồng nhau sẽ vẫn được truyền bằng tham chiếu.

Việc thêm hoặc sửa đổi các thuộc tính trên một đối tượng hiện có theo kiểu bất biến được đơn giản hóa với spread. Trong ví dụ này, thuộc tính isLoggedIn được thêm vào đối tượng user:

const user = { id: 3, name: 'Ron', } const updatedUser = { ...user, isLoggedIn: true } console.log(updatedUser)

Nó sẽ xuất ra như sau:

Output

{id: 3, name: "Ron", isLoggedIn: true}

Một điều quan trọng cần lưu ý với việc cập nhật các đối tượng thông qua spread là bất kỳ đối tượng lồng nhau nào cũng sẽ phải được spread. Ví dụ, giả sử rằng trong đối tượng user có một đối tượng organization:

const user = { id: 3, name: 'Ron', organization: { name: 'Parks & Recreation', city: 'Pawnee', }, }

Nếu bạn cố thêm một mục mới vào organization, nó sẽ ghi đè lên các trường hiện có:

const updatedUser = { ...user, organization: { position: 'Director' } } console.log(updatedUser)

Kết quả:

Output

id: 3 name: "Ron" organization: {position: "Director"}

Nếu khả năng thay đổi không phải là vấn đề, trường có thể được cập nhật trực tiếp:

user.organization.position = 'Director'

Nhưng vì chúng ta đang tìm kiếm một giải pháp bất biến, nên chúng ta có thể spread đối tượng bên trong để giữ lại các thuộc tính hiện có:

const updatedUser = { ...user, organization: { ...user.organization, position: 'Director', }, } console.log(updatedUser)

Kết quả:

Output

id: 3

name: "Ron"

organization: {name: "Parks & Recreation", city: "Pawnee", position: "Director"}

Spread với các lời gọi hàm

Spread cũng có thể được sử dụng với các đối số trong các lời gọi hàm.

Ví dụ, ta định nghĩa hàm multiply ba tham số và trả về tích của chúng:

function multiply(a, b, c) { return a * b * c }

Thông thường, bạn sẽ truyền ba giá trị riêng lẻ làm đối số cho lệnh gọi hàm như ví dụ sau:

multiply(1, 2, 3)

Kết quả:

Output

6

Tuy nhiên, nếu tất cả các giá trị bạn muốn truyền cho hàm đã tồn tại trong một mảng, thì cú pháp spread cho phép bạn sử dụng từng mục trong một mảng làm đối số:

const numbers = [1, 2, 3] multiply(...numbers)

Kết quả:

Output

6

Lưu ý: Ngoài sử dụng spread thì ta cũng có thể sử dụng apply():

multiply.apply(null, [1, 2, 3])

Kết quả:

Output

6

Đến đây thì bạn đã thấy cách spread có thể rút ngắn mã của bạn, bạn có thể xem cách sử dụng khác của cú pháp ... là tham số rest.

Tham số rest

Tính năng cuối cùng bạn sẽ học trong bài viết này là cú pháp tham số rest. Cú pháp xuất hiện giống như spread ( ...) nhưng có tác dụng ngược lại. Thay vì giải nén một mảng hoặc đối tượng thành các giá trị riêng lẻ, cú pháp rest sẽ tạo một mảng với số lượng đối số không xác định.

Ví dụ trong hàm restTest dưới đây, nếu chúng ta muốn args là một mảng bao gồm một số lượng đối số không xác định, thì chúng ta có thể làm như sau:

function restTest(...args) { console.log(args) } restTest(1, 2, 3, 4, 5, 6)

Tất cả các đối số được truyền cho hàm restTest hiện có sẵn trong mảng args:

Output

[1, 2, 3, 4, 5, 6]

Cú pháp Rest có thể được sử dụng làm tham số duy nhất hoặc tham số cuối cùng trong danh sách. Nếu được sử dụng làm tham số duy nhất, nó sẽ thu thập tất cả các đối số, nhưng nếu nó ở cuối danh sách, nó sẽ thu thập mọi đối số còn lại, như được thấy trong ví dụ này:

function restTest(one, two, ...args) { console.log(one) console.log(two) console.log(args) } restTest(1, 2, 3, 4, 5, 6)

Thao tác này sẽ lấy riêng hai đối số đầu tiên, sau đó nhóm các đối số còn lại thành một mảng:

Output

1 2 [3, 4, 5, 6]

Trong đoạn code dưới đây, biến arguments có thể được sử dụng để thu thập tất cả các đối số được truyền tới hàm:

function testArguments() { console.log(arguments) } testArguments('how', 'many', 'arguments')

Kết quả:

Output

1Arguments(3) ["how", "many", "arguments"]

Tuy nhiên, điều này có một vài nhược điểm. Đầu tiên, biến arguments không thể được sử dụng với các hàm mũi tên.

const testArguments = () => { console.log(arguments) } testArguments('how', 'many', 'arguments')

Điều này sẽ dẫn đến một lỗi:

Output

Uncaught ReferenceError: arguments is not defined

Ngoài ra, arguments không phải là một mảng thực sự và không thể sử dụng các phương thức như map và filter không được chuyển đổi đầu tiên thành một mảng. Nó cũng sẽ thu thập tất cả các đối số được truyền thay vì chỉ phần còn lại của các đối số, như đã thấy trong ví dụ restTest(one, two, ...args) ở trên.

Rest cũng có thể được sử dụng khi cấu trúc lại mảng:

const [firstTool, ...rest] = ['hammer', 'screwdriver', 'wrench'] console.log(firstTool) console.log(rest)

Kết quả:

Output

hammer ["screwdriver", "wrench"]

Và rest cũng có thể được sử dụng khi cấu trúc các đối tượng:

const { isLoggedIn, ...rest } = { id: 1, name: 'Ben', isLoggedIn: true } console.log(isLoggedIn) console.log(rest)

Kết quả:

Output

true {id: 1, name: "Ben"}

Bằng cách này, cú pháp rest cung cấp các phương pháp hiệu quả để thu thập một lượng item không xác định.

Phần kết luận

Trong bài viết này, ta đã tìm hiểu về cấu trúc destructuring, cú pháp spread và phần còn lại. Tóm tắt:

  • Destructuring (Cấu trúc hủy) được sử dụng để tạo các biến thể từ các item mảng hoặc thuộc tính của đối tượng.
  • Cú pháp Spread được sử dụng để giải nén các tệp lặp như mảng, đối tượng và lời gọi hàm.
  • Cú pháp tham số rest sẽ tạo một mảng từ một số lượng giá trị không xác định.

Cấu trúc destructuring, tham số rest và cú pháp spread là những tính năng hữu ích trong JavaScript giúp giữ cho code của bạn ngắn gọn và sạch sẽ.

Nếu bạn muốn xem hành động của cấu trúc hủy, hãy xem Cách tùy chỉnh component với prop, sử dụng cú pháp này để hủy cấu trúc dữ liệu và truyền nó đến các thành phần giao diện người dùng tùy chỉnh. Nếu bạn muốn tìm hiểu thêm về JavaScript, hãy quay lại trang Cách viết mã trong loạt bài JavaScript của chúng tôi.

Từ khóa » Toán Tử Spread Trong Javascript