diff --git a/1-js/06-advanced-functions/02-rest-parameters-spread/article.md b/1-js/06-advanced-functions/02-rest-parameters-spread/article.md index 7089a6750..696f79960 100644 --- a/1-js/06-advanced-functions/02-rest-parameters-spread/article.md +++ b/1-js/06-advanced-functions/02-rest-parameters-spread/article.md @@ -1,36 +1,38 @@ -# Rest parameters and spread syntax +# Rest 參數與 Spread 語法 -Many JavaScript built-in functions support an arbitrary number of arguments. +許多 JavaScript 內建函式支援任意數量的參數。 -For instance: +例如: -- `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)` -- 返回參數中最大的值。 +- `Object.assign(dest, src1, ..., srcN)` -- 複製 `src1..N` 的屬性到 `dest`。 +- ...等。 -In this chapter we'll learn how to do the same. And also, how to pass arrays to such functions as parameters. +在本章中,我們將學習如何做到這一點。 並且,如何將陣列作為參數傳遞給這樣的函式。 -## Rest parameters `...` +## Rest 參數 `...` -A function can be called with any number of arguments, no matter how it is defined. +一個函式可以被任意數量的參數呼叫,不管它是如何定義的。 + +如下: -Like here: ```js run function sum(a, b) { return a + b; } -alert( sum(1, 2, 3, 4, 5) ); +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. +這裡並不會因為過多的參數而產生錯誤。 但這裡的結果只會計算前兩個。 -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". +剩下的參數可以在函式定義中使用三個點 `...` 並跟著包含它們的陣列名稱。 這些點字面上意味著「將剩餘的參數收集到陣列中」。 -For instance, to gather all arguments into array `args`: +例如,將所有參數收集到陣列 `args` 中: ```js run -function sumAll(...args) { // args is the name for the array +function sumAll(...args) { + // args 是陣列的名稱 let sum = 0; for (let arg of args) sum += arg; @@ -38,31 +40,31 @@ function sumAll(...args) { // args is the name for the array return sum; } -alert( sumAll(1) ); // 1 -alert( sumAll(1, 2) ); // 3 -alert( sumAll(1, 2, 3) ); // 6 +alert(sumAll(1)); // 1 +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. +我們可以選擇將第一個參數作為變數,並只收集剩下的參數。 -Here the first two arguments go into variables and the rest go into `titles` array: +這裡的前兩個參數會進入變數,其餘的則會進入 `titles` 陣列: ```js run function showName(firstName, lastName, ...titles) { - alert( firstName + ' ' + lastName ); // Julius Caesar + alert(firstName + " " + lastName); // Julius Caesar - // the rest go into titles array + // 剩餘的參數會被收集到陣列中 // i.e. titles = ["Consul", "Imperator"] - alert( titles[0] ); // Consul - alert( titles[1] ); // Imperator - alert( titles.length ); // 2 + alert(titles[0]); // Consul + alert(titles[1]); // Imperator + alert(titles.length); // 2 } showName("Julius", "Caesar", "Consul", "Imperator"); ``` -````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="剩餘的參數必須在最後" +Rest 參數會收集所有剩餘的參數,所以以下的寫法是不合理的,並會產生錯誤: ```js function f(arg1, ...rest, arg2) { // arg2 after ...rest ?! @@ -70,73 +72,78 @@ function f(arg1, ...rest, arg2) { // arg2 after ...rest ?! } ``` -The `...rest` must always be last. +`...rest` 必須在參數最後。 ```` -## The "arguments" variable +## "arguments" 變數 -There is also a special array-like object named `arguments` that contains all arguments by their index. +這裡也有一個名為 `arguments` 的特殊陣列式物件,它按索引包含所有參數。 -For instance: +例如: ```js run function showName() { - alert( arguments.length ); - alert( arguments[0] ); - alert( arguments[1] ); + alert(arguments.length); + alert(arguments[0]); + alert(arguments[1]); - // it's iterable + // 可遍歷的 // for(let arg of arguments) alert(arg); } -// shows: 2, Julius, Caesar +// 依序顯示: 2, Julius, Caesar showName("Julius", "Caesar"); -// shows: 1, Ilya, undefined (no second argument) +// 依序顯示: 1, Ilya, undefined (無第二個參數) 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. +過去,rest 參數並不存在於 JavaScript 中,使用 `arguments` 是取得函式所有參數的唯一方法。 這仍然有效,我們可以在舊程式碼中找到它。 + +````warn header="`arguments` 是可遍歷的,但不是陣列" +但缺點是,`arguments`同時是陣列式的和可遍歷的,但它不是陣列。 因此不支援陣列方法,所以我們不能呼叫`arguments.map(...)` 等方法。 + +````` + +此外,它包含所有參數。 我們無法像使用 rest 參數只擷取部分參數。 -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. +因此,當我們需要這些功能時,建議優先使用 rest 參數。 -Also, it always contains all arguments. We can't capture them partially, like we did with rest parameters. +````smart header="箭頭函式沒有 `\"arguments\"`" -So when we need these features, then rest parameters are preferred. +如果我們在箭頭函式中存取 `arguments` 物件,它會從外部「正常」函式中取得。 -````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. -Here's an example: +例如: ```js run function f() { - let showArg = () => alert(arguments[0]); - showArg(); + let showArg = () => alert(arguments[0]); + showArg(); } 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. -```` +還記得嗎?箭頭函式沒有自己的 `this`。現在我們知道它們也沒有特殊的 `arguments` 物件。 +````` ## Spread syntax [#spread-syntax] -We've just seen how to get an array from the list of parameters. +我們已經看到如何從參數列表中取得陣列。 -But sometimes we need to do exactly the reverse. +但有時候我們也需要做相反的事情。 -For instance, there's a built-in function [Math.max](mdn:js/Math/max) that returns the greatest number from a list: +例如, 內建函式 [Math.max](mdn:js/Math/max) 會返回列表中最大數值: ```js run -alert( Math.max(3, 5, 1) ); // 5 +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? +假設我們有一組陣列 `[3, 5, 1]`。 我們如何使用它呼叫 `Math.max`? -Passing it "as is" won't work, because `Math.max` expects a list of numeric arguments, not a single array: +原封不動地傳入這組陣列是不行的,因為 `Math.max` 預期的是一組數值參數,而不是單一個陣列: ```js run let arr = [3, 5, 1]; @@ -146,40 +153,39 @@ 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. +我們不能在程式碼中手動列出項目 `Math.max(arr[0], arr[1], arr[2])`,因為並不能確定有多少項,當程式碼執行時,可能有很多,也可能沒有,這會導致程式碼將變得很醜。 -*Spread syntax* to the rescue! It looks similar to rest parameters, also using `...`, but does quite the opposite. +_Spread 語法_ 就是解方! 它和 Rest 參數很像,也使用 `...`,但用途相反。 -When `...arr` is used in the function call, it "expands" an iterable object `arr` into the list of arguments. +當 `...arr` 在函式呼叫中使用時,它會將可迭代物件 `arr` 展開成參數列表。 -For `Math.max`: +以 `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 (spread 將陣列轉換為參數列表) ``` -We also can pass multiple iterables this way: +我們也可以這樣傳入多個可迭代物件: ```js run let arr1 = [1, -2, 3, 4]; let arr2 = [8, 3, -8, 1]; -alert( Math.max(...arr1, ...arr2) ); // 8 +alert(Math.max(...arr1, ...arr2)); // 8 ``` -We can even combine the spread syntax with normal values: - +我們甚至可以將 spread 語法和正常的值結合: ```js run let arr1 = [1, -2, 3, 4]; let arr2 = [8, 3, -8, 1]; -alert( Math.max(1, ...arr1, 2, ...arr2, 25) ); // 25 +alert(Math.max(1, ...arr1, 2, ...arr2, 25)); // 25 ``` -Also, the spread syntax can be used to merge arrays: +另外,spread 語法也可以用來合併陣列: ```js run let arr = [3, 5, 1]; @@ -189,101 +195,99 @@ 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, 接著是 arr, 然後是 2, 再接著是 arr2) ``` -In the examples above we used an array to demonstrate the spread syntax, but any iterable will do. +在這個範例中,我們使用陣列來展示 spread 語法,但任何可迭代物件都可以。 -For instance, here we use the spread syntax to turn the string into array of characters: +例如,這裡我們使用 spread 語法將字串轉換為字元陣列: ```js run let str = "Hello"; -alert( [...str] ); // H,e,l,l,o +alert([...str]); // H,e,l,l,o ``` -The spread syntax internally uses iterators to gather elements, the same way as `for..of` does. +spread 語法內部使用迭代器 (iterator) 來收集元素,就像 `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]`. +因此,對於字串來說,`for...of` 會回傳字元,而 `...str` 則會變成 `"H","e","l","l","o"`。 字元列表會傳入陣列初始化器 `[...str]`。 -For this particular task we could also use `Array.from`, because it converts an iterable (like a string) into an array: +針對這個特別的任務,我們也可以使用 `Array.from`,因為它會將可迭代物件(像是字串)轉換為陣列: ```js run let str = "Hello"; -// Array.from converts an iterable into an array -alert( Array.from(str) ); // H,e,l,l,o +// Array.from 將可迭代物件轉換為陣列 +alert(Array.from(str)); // H,e,l,l,o ``` -The result is the same as `[...str]`. +執行後結果和 `[...str]` 一致。 -But there's a subtle difference between `Array.from(obj)` and `[...obj]`: +不過 `Array.from(obj)` 和 `[...obj]` 之間有一個微妙的差異: -- `Array.from` operates on both array-likes and iterables. -- The spread syntax works only with iterables. +- `Array.form` 可以操作陣列式物件和可迭代物件。 +- Spread 語法只能操作可迭代物件。 -So, for the task of turning something into an array, `Array.from` tends to be more universal. +因此,對於將"某些東西"轉換為陣列的任務,`Array.from` 通常更被廣泛使用。 +## 複製物件 (object) / 陣列 (array) -## Get a new copy of an object/array +還記得我們[先前](https://javascript.info/object#cloning-and-merging-object-assign)提到的 `Object.assign()` 嗎? -Remember when we talked about `Object.assign()` [in the past](https://javascript.info/object#cloning-and-merging-object-assign)? - -It is possible to do the same thing with the spread operator! +使用 spread 語法也可以做到相同的事情!(註:淺拷貝) ```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]; // 將陣列展開成參數列表 +// 在新陣列返回結果 -// do the arrays have the same contents? +// 兩個陣列的內容相同嗎? alert(JSON.stringify(arr) === JSON.stringify(arrCopy)); // true -// are the arrays equal? -alert(arr === arrCopy); // false (not same reference) +// 兩個陣列相等嗎? +alert(arr === arrCopy); // false (非同一個參考) -// modifying our initial array does not modify the copy: +// 修改原始陣列不會影響副本: 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: +注意,用相同的方式來複製一個物件也是可行的。 ```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 }; // 展開物件成參數列表 +// 在新物件返回結果 -// do the objects have the same contents? +// d兩個物件的內容相同嗎? alert(JSON.stringify(obj) === JSON.stringify(objCopy)); // true -// are the objects equal? +// 兩個物件相等嗎? alert(obj === objCopy); // false (not same reference) -// modifying our initial object does not modify the copy: +// 修改原始物件不會影響副本: 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. - +以這樣的方式複製一個物件比 `let objCopy = Object.assign({}, obj);` 或是複製一個陣列 `let arrCopy = Object.assign([], arr);` 來得簡潔,所以我們優先使用它。 -## Summary +## 結論 -When we see `"..."` in the code, it is either rest parameters or the spread syntax. +當我們在程式碼中看到 `...`,它可能是 rest 參數或是 spread 語法。 -There's an easy way to distinguish between them: +有一個簡單的方法可以區分它們: -- 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. +- 當 `...` 在一個函式參數的最後,它是一個 "rest 參數",並且會將參數列表中剩餘的參數收集到一個陣列中。 +- 當 `...` 在函式呼叫或類似的地方出現時,它被稱為 "spread 語法",並且會將陣列展開成參數列表。 -Use patterns: +應用場景: -- 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. +- Rest 參數用來建立可以接受任意數量參數的函式。 +- spread 語法用來將陣列傳入一個需要多個參數的函式。 -Together they help to travel between a list and an array of parameters with ease. +兩者並行可以讓我們在陣列和參數列表之間輕鬆地切換。 -All arguments of a function call are also available in "old-style" `arguments`: array-like iterable object. +所有函式呼叫的參數都可以在 "舊式" 的 `arguments` 中使用: 類似陣列的可迭代物件。