Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rest parameters and spread syntax #200

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 92 additions & 92 deletions 1-js/06-advanced-functions/02-rest-parameters-spread/article.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
# Rest parameters and spread syntax
# Tham số còn lại và cú pháp lan ra

Many JavaScript built-in functions support an arbitrary number of arguments.
Nhiều hàm dựng sẵn trong JavaScript hỗ trợ số lượng đối số tùy ý.

For instance:
Ví dụ:

- `Math.max(arg1, arg2, ..., argN)` -- returns the greatest of the arguments.
- `Object.assign(dest, src1, ..., srcN)` -- copies properties from `src1..N` into `dest`.
- ...and so on.
- `Math.max(arg1, arg2, ..., argN)` -- trả về giá trị lớn nhất của các đối số.
- `Object.assign(dest, src1, ..., srcN)` -- sao chép các thuộc tính từ `src1..N` vào `dest`.
- ...v.v.

In this chapter we'll learn how to do the same. And also, how to pass arrays to such functions as parameters.
Trong chương này chúng ta sẽ học cách làm điều tương tự. Và cách truyền array cho các hàm như tham số.

## Rest parameters `...`
## Thông số còn lại `...`

A function can be called with any number of arguments, no matter how it is defined.
Một hàm có thể được gọi với bất kỳ số lượng đối số nào, bất kể nó được định nghĩa như thế nào.

Like here:
Như ở đây:
```js run
function sum(a, b) {
return a + b;
Expand All @@ -23,14 +23,14 @@ function sum(a, b) {
alert( sum(1, 2, 3, 4, 5) );
```

There will be no error because of "excessive" arguments. But of course in the result only the first two will be counted.
Sẽ không có lỗi vì "thừa" đối số. Nhưng tất nhiên trong kết quả chỉ có hai cái đầu tiên sẽ được tính.

The rest of the parameters can be included in the function definition by using three dots `...` followed by the name of the array that will contain them. The dots literally mean "gather the remaining parameters into an array".
Phần còn lại của các tham số có thể được bao gồm trong định nghĩa hàm bằng cách sử dụng ba dấu chấm `...` theo sau là tên của array sẽ chứa chúng. Các dấu chấm có nghĩa đen là "tập hợp các tham số còn lại thành một mảng".

For instance, to gather all arguments into array `args`:
Chẳng hạn, để tập hợp tất cả các đối số vào array `args`:

```js run
function sumAll(...args) { // args is the name for the array
function sumAll(...args) { // args là tên của array
let sum = 0;

for (let arg of args) sum += arg;
Expand All @@ -43,71 +43,71 @@ alert( sumAll(1, 2) ); // 3
alert( sumAll(1, 2, 3) ); // 6
```

We can choose to get the first parameters as variables, and gather only the rest.
Chúng ta có thể chọn lấy các tham số đầu tiên làm biến và chỉ thu thập phần còn lại.

Here the first two arguments go into variables and the rest go into `titles` array:
Ở đây, hai đối số đầu tiên đi vào các biến và phần còn lại đi vào array `titles`:

```js run
function showName(firstName, lastName, ...titles) {
alert( firstName + ' ' + lastName ); // Julius Caesar

// the rest go into titles array
// i.e. titles = ["Consul", "Imperator"]
alert( titles[0] ); // Consul
alert( titles[1] ); // Imperator
// phần còn lại đi vào mảng tiêu đề
// tức là titles = ["Lãnh đạo", "Đế chế"]
alert( titles[0] ); // Lãnh đạo
alert( titles[1] ); // Đế chế
alert( titles.length ); // 2
}

showName("Julius", "Caesar", "Consul", "Imperator");
showName("Julius", "Caesar", "Lãnh đạo", "Đế chế");
```

````warn header="The rest parameters must be at the end"
The rest parameters gather all remaining arguments, so the following does not make sense and causes an error:
````warn header="Các tham số còn lại phải ở cuối"
Các tham số còn lại tập hợp tất cả các đối số còn lại, vì vậy những điều sau đây không có ý nghĩa và gây ra lỗi:

```js
function f(arg1, ...rest, arg2) { // arg2 after ...rest ?!
// error
// lỗi
}
```

The `...rest` must always be last.
`...rest` phải luôn ở cuối cùng.
````

## The "arguments" variable
## Biến "đối số"

There is also a special array-like object named `arguments` that contains all arguments by their index.
Ngoài ra còn có một đối tượng dạng array đặc biệt có tên `arguments` chứa tất cả các đối số theo chỉ mục của chúng.

For instance:
Ví dụ:

```js run
function showName() {
alert( arguments.length );
alert( arguments[0] );
alert( arguments[1] );

// it's iterable
// nó có thể lặp lại
// for(let arg of arguments) alert(arg);
}

// shows: 2, Julius, Caesar
// cho thấy: 2, Julius, Caesar
showName("Julius", "Caesar");

// shows: 1, Ilya, undefined (no second argument)
// hiển thị: 1, Ilya, undefined (không có đối số thứ hai)
showName("Ilya");
```

In old times, rest parameters did not exist in the language, and using `arguments` was the only way to get all arguments of the function. And it still works, we can find it in the old code.
Trước đây, các tham số còn lại không tồn tại trong ngôn ngữ và sử dụng `arguments` là cách duy nhất để lấy tất cả các đối số của hàm. Và nó vẫn hoạt động, chúng ta có thể tìm thấy nó trong mã cũ.

But the downside is that although `arguments` is both array-like and iterable, it's not an array. It does not support array methods, so we can't call `arguments.map(...)` for example.
Nhưng nhược điểm là mặc dù `arguments` vừa có dạng array vừa có thể lặp lại, nhưng nó không phải là array. Nó không hỗ trợ các phương thức array, vì vậy chúng ta không thể gọi `arguments.map(...)` chẳng hạn.

Also, it always contains all arguments. We can't capture them partially, like we did with rest parameters.
Ngoài ra, nó luôn chứa tất cả các đối số. Chúng ta không thể chụp chúng một phần, giống như chúng ta đã làm với các tham số còn lại.

So when we need these features, then rest parameters are preferred.
Vì vậy, khi chúng ta cần các tính năng này, thì các tham số còn lại được ưu tiên.

````smart header="Arrow functions do not have `\"arguments\"`"
If we access the `arguments` object from an arrow function, it takes them from the outer "normal" function.
````smart header="Arrow functions không có `\"arguments\"`"
Nếu chúng ta truy cập đối tượng `đối số` từ một arrow function, thì nó sẽ lấy chúng từ hàm "bình thường" bên ngoài.

Here's an example:
Đây là 1 ví dụ:

```js run
function f() {
Expand All @@ -118,25 +118,25 @@ function f() {
f(1); // 1
```

As we remember, arrow functions don't have their own `this`. Now we know they don't have the special `arguments` object either.
Như chúng ta đã nhớ, arrow function không có `this` của riêng chúng. Bây giờ chúng ta biết rằng chúng cũng không có đối tượng `arguments` đặc biệt.
````


## Spread syntax [#spread-syntax]
## Cú pháp lan rộng [#spread-syntax]

We've just seen how to get an array from the list of parameters.
Chúng ta vừa xem cách lấy một array từ danh sách các tham số.

But sometimes we need to do exactly the reverse.
Nhưng đôi khi chúng ta cần làm ngược lại.

For instance, there's a built-in function [Math.max](mdn:js/Math/max) that returns the greatest number from a list:
Chẳng hạn, có một hàm tích hợp [Math.max](mdn:js/Math/max) trả về số lớn nhất từ một danh sách:

```js run
alert( Math.max(3, 5, 1) ); // 5
```

Now let's say we have an array `[3, 5, 1]`. How do we call `Math.max` with it?
Bây giờ, giả sử chúng ta có một array `[3, 5, 1]`. Làm thế nào để chúng ta gọi `Math.max` với nó?

Passing it "as is" won't work, because `Math.max` expects a list of numeric arguments, not a single array:
Chuyển nó "nguyên trạng" sẽ không hoạt động, bởi vì `Math.max` mong đợi một danh sách các đối số, không phải một array đơn lẻ:

```js run
let arr = [3, 5, 1];
Expand All @@ -146,21 +146,21 @@ alert( Math.max(arr) ); // NaN
*/!*
```

And surely we can't manually list items in the code `Math.max(arr[0], arr[1], arr[2])`, because we may be unsure how many there are. As our script executes, there could be a lot, or there could be none. And that would get ugly.
Và chắc chắn chúng ta không thể liệt kê thủ công các mục trong mã `Math.max(arr[0], arr[1], arr[2])`, bởi vì chúng ta có thể không chắc có bao nhiêu mục. Khi tập lệnh của chúng ta thực thi, có thể có rất nhiều hoặc không có gì. Và điều đó sẽ trở nên xấu xí.

*Spread syntax* to the rescue! It looks similar to rest parameters, also using `...`, but does quite the opposite.
*Cú pháp lan rộng* tới để giải cứu! Nó trông tương tự như các tham số còn lại, cũng sử dụng `...`, nhưng hoàn toàn ngược lại.

When `...arr` is used in the function call, it "expands" an iterable object `arr` into the list of arguments.
Khi `...arr` được sử dụng trong lệnh gọi hàm, nó sẽ "mở rộng" một đối tượng có thể lặp lại `arr` vào danh sách các đối số.

For `Math.max`:
Đối với `Math.max`:

```js run
let arr = [3, 5, 1];

alert( Math.max(...arr) ); // 5 (spread turns array into a list of arguments)
alert( Math.max(...arr) ); // 5 (lan rộng biến array thành một danh sách các đối số)
```

We also can pass multiple iterables this way:
Chúng ta cũng có thể vượt qua nhiều iterable theo cách này:

```js run
let arr1 = [1, -2, 3, 4];
Expand All @@ -169,7 +169,7 @@ let arr2 = [8, 3, -8, 1];
alert( Math.max(...arr1, ...arr2) ); // 8
```

We can even combine the spread syntax with normal values:
Chúng ta thậm chí có thể kết hợp cú pháp lan rộng với các giá trị bình thường:


```js run
Expand All @@ -179,7 +179,7 @@ let arr2 = [8, 3, -8, 1];
alert( Math.max(1, ...arr1, 2, ...arr2, 25) ); // 25
```

Also, the spread syntax can be used to merge arrays:
Ngoài ra, cú pháp lan rộng có thể được sử dụng để hợp nhất các array:

```js run
let arr = [3, 5, 1];
Expand All @@ -189,107 +189,107 @@ let arr2 = [8, 9, 15];
let merged = [0, ...arr, 2, ...arr2];
*/!*

alert(merged); // 0,3,5,1,2,8,9,15 (0, then arr, then 2, then arr2)
alert(merged); // 0,3,5,1,2,8,9,15 (0, sau đó arr, sau đó 2, sau đó arr2)
```

In the examples above we used an array to demonstrate the spread syntax, but any iterable will do.
Trong các ví dụ trên, chúng ta đã sử dụng một array để minh họa cú pháp lan rộng, nhưng bất kỳ iterable nào cũng được.

For instance, here we use the spread syntax to turn the string into array of characters:
Chẳng hạn, ở đây chúng ta sử dụng cú pháp lan rộng để biến chuỗi thành array ký tự:

```js run
let str = "Hello";

alert( [...str] ); // H,e,l,l,o
```

The spread syntax internally uses iterators to gather elements, the same way as `for..of` does.
Cú pháp lan rộng bên trong sử dụng các trình lặp để thu thập các phần tử, giống như cách thực hiện của `for..of`.

So, for a string, `for..of` returns characters and `...str` becomes `"H","e","l","l","o"`. The list of characters is passed to array initializer `[...str]`.
Vì vậy, đối với một chuỗi, `for..of` trả về các ký tự và `...str` trở thành `"H","e","l","l","o"`. Danh sách các ký tự được chuyển đến bộ khởi tạo array `[...str]`.

For this particular task we could also use `Array.from`, because it converts an iterable (like a string) into an array:
Đối với tác vụ cụ thể này, chúng ta cũng có thể sử dụng `Array.from`, bởi vì nó chuyển đổi một iterable (như một chuỗi) thành một array:

```js run
let str = "Hello";

// Array.from converts an iterable into an array
// Array.from chuyển đổi một iterable thành một array
alert( Array.from(str) ); // H,e,l,l,o
```

The result is the same as `[...str]`.
Kết quả giống như `[...str]`.

But there's a subtle difference between `Array.from(obj)` and `[...obj]`:
Nhưng có một sự khác biệt nhỏ giữa `Array.from(obj)` `[...obj]`:

- `Array.from` operates on both array-likes and iterables.
- The spread syntax works only with iterables.
- `Array.from` hoạt động trên cả dạng array và iterable.
- Cú pháp trải rộng chỉ hoạt động với các iterable.

So, for the task of turning something into an array, `Array.from` tends to be more universal.
Vì vậy, đối với nhiệm vụ biến một thứ gì đó thành một array, `Array.from` có xu hướng phổ biến hơn.


## Copy an array/object
## Sao chép một mảng/đối tượng

Remember when we talked about `Object.assign()` [in the past](info:object-copy#cloning-and-merging-object-assign)?
Bạn có nhớ khi chúng ta nói về `Object.assign()` [trong quá khứ](info:object-copy#cloning-and-merging-object-assign) không?

It is possible to do the same thing with the spread syntax.
Có thể làm điều tương tự với cú pháp lan rộng.

```js run
let arr = [1, 2, 3];

*!*
let arrCopy = [...arr]; // spread the array into a list of parameters
// then put the result into a new array
let arrCopy = [...arr]; // trải array thành một danh sách các tham số
// sau đó đặt kết quả vào một array mới
*/!*

// do the arrays have the same contents?
// các array có cùng nội dung không?
alert(JSON.stringify(arr) === JSON.stringify(arrCopy)); // true

// are the arrays equal?
alert(arr === arrCopy); // false (not same reference)
// các array có bằng nhau không?
alert(arr === arrCopy); // false (không cùng tham chiếu)

// modifying our initial array does not modify the copy:
// sửa đổi array ban đầu của chúng ta không sửa đổi bản sao:
arr.push(4);
alert(arr); // 1, 2, 3, 4
alert(arrCopy); // 1, 2, 3
```

Note that it is possible to do the same thing to make a copy of an object:
Lưu ý rằng có thể làm điều tương tự để tạo một bản sao của đối tượng:

```js run
let obj = { a: 1, b: 2, c: 3 };

*!*
let objCopy = { ...obj }; // spread the object into a list of parameters
// then return the result in a new object
let objCopy = { ...obj }; // trải đối tượng vào một danh sách các tham số
// sau đó trả về kết quả trong một đối tượng mới
*/!*

// do the objects have the same contents?
// các đối tượng có cùng nội dung không?
alert(JSON.stringify(obj) === JSON.stringify(objCopy)); // true

// are the objects equal?
alert(obj === objCopy); // false (not same reference)
// các đối tượng có bằng nhau không?
alert(obj === objCopy); // false (không cùng tham chiếu)

// modifying our initial object does not modify the copy:
// sửa đổi đối tượng ban đầu của chúng ta không sửa đổi bản sao:
obj.d = 4;
alert(JSON.stringify(obj)); // {"a":1,"b":2,"c":3,"d":4}
alert(JSON.stringify(objCopy)); // {"a":1,"b":2,"c":3}
```

This way of copying an object is much shorter than `let objCopy = Object.assign({}, obj)` or for an array `let arrCopy = Object.assign([], arr)` so we prefer to use it whenever we can.
Cách sao chép một đối tượng này ngắn hơn nhiều so với `let objCopy = Object.assign({}, obj)` hoặc cho một array `let arrCopy = Object.assign([], arr)` vì vậy chúng ta thích sử dụng nó bất cứ khi nào chúng ta có thể.


## Summary
## Tóm tắt

When we see `"..."` in the code, it is either rest parameters or the spread syntax.
Khi chúng ta thấy `"..."` trong mã, đó là tham số còn lại hoặc cú pháp lan rộng.

There's an easy way to distinguish between them:
Có một cách dễ dàng để phân biệt giữa chúng:

- When `...` is at the end of function parameters, it's "rest parameters" and gathers the rest of the list of arguments into an array.
- When `...` occurs in a function call or alike, it's called a "spread syntax" and expands an array into a list.
- Khi `...` ở cuối các tham số hàm, nó là "tham số còn lại" và tập hợp phần còn lại của danh sách các đối số vào một array.
- Khi `...` xuất hiện trong một lệnh gọi hàm hoặc tương tự, nó được gọi là "cú pháp lan rộng" và mở rộng một array thành một danh sách.

Use patterns:
Sử dụng các mẫu:

- Rest parameters are used to create functions that accept any number of arguments.
- The spread syntax is used to pass an array to functions that normally require a list of many arguments.
- Các tham số còn lại được sử dụng để tạo các hàm chấp nhận bất kỳ số lượng đối số nào.
- Cú pháp lan rộng được sử dụng để truyền một array cho các hàm thường yêu cầu một danh sách nhiều đối số.

Together they help to travel between a list and an array of parameters with ease.
Chúng cùng nhau giúp di chuyển giữa một danh sách và một array các tham số một cách dễ dàng.

All arguments of a function call are also available in "old-style" `arguments`: array-like iterable object.
Tất cả các đối số của một lệnh gọi hàm cũng có sẵn trong `arguments`: đối tượng dạng mảng có thể lặp lại.