[ES6 入門] 解構、展開、剩餘參數


Posted by Lauviah0622 on 2020-07-29

很多人(可能其實只有我自己),在學完基礎的 JS 之後,為了跟風或者是找工作,一腳踏入進入 React 的世界,結果卻發現看了範例卻傻了,根本不知道在幹嘛。不是那種慢慢理解就可以的狀況,是連語法都看不懂。

其實也沒有那麼複雜,就是 es6 的語法不熟而已, es6 相較於之前的 javascript 的語法根本就是一個大躍進。各種以前想像不到的神奇都出現了,有些有用到,有些沒用到(很快就會開始用到)。今天講的內容不敢說很常用(相較於 const, let, arrow function 等等的語法起來的確沒有那麼常用),但是可以幫你省下很多繁瑣的過程。

分別是

  • Destructuring assignment
  • Spread Syntax
  • Rest Parameter

YA 那就開始囉。

Destructuring assignment

中文應該稱作解構賦值吧。聽起來超帥的,很像某個技能名稱

先來看 code 好了

let [a, b] = [10, 20];
console.log(a, b) // 10, 20

let {name, phone} = {name: 'Jack', phone: '0920123321'};
console.log(name, phone) // 'Jack' '0920123321'

看了之後就知道其實沒有那麼複雜,就是把 Array 或者是 Object 拆分成獨立的變數的一種方法而已。

那下面就來比較詳細的介紹。

array 的 Destructuring

其實光上面的範例看應該可以看出一些端倪。我們宣告一個有兩個變數 a, b 的 array。

let [a, b]

然後再 assign = 給另一個已經有值的 array。

let [a, b] = [10, 20];

然後 array 中的 a, b 就被賦值

let [a, b] = [10, 20];

console.log(a, b) //10 20
//相當於
// let a = 10;
// let b = 20;

變數會依照順序對應到 array 中的值。雖然一開始看到可能會有:「What?你怎麼把奇怪的東西兜在一起了?」但習慣之後應該算是蠻直觀的。

object 的 Destructuring

然後在 Object 中也有解構賦值。來試試吧,首先向剛剛的 array 一樣在 object 中放入你要的變數。

let {c, d} = {name: 'Jack', phone: '0920123321'}

只不過是把 [ ] 換成 { }而已嘛,簡單。看我 print 出來。

let {c, d} = {name: 'Jack', phone: '0920123321'};

console.log(c, d) //undefined undefined

什麼!為什麼是 undefined!搞屁啊!

array 利用順序來對應哪個變數名稱對應到哪個值,那 Object 也應該要有個東西對應才對,然而 object 內容不像 array 一樣,是沒有順序的,所以我們必須有個東西作為對應,那東西就是 Object 的 key。

因此 Object 的變數名稱必須放在 value裡面,像是:

const obj = { name: 'Jack', phone: '0920123321' };

let { name: JackName, phone: JackPhone } = obj;

console.log(JackName, JackPhone) //'Jack' '0920123321'

欸不是!剛剛的範例也沒看你把要賦值的變數名稱放在 value 啊!為什麼還是可以用呢?

let { name, phone } = { name: 'Jack', phone: '0920123321' };

console.log(name, phone);

先別著急,還記得 object 有一個用法嗎?如果你要把變數的名稱作為 key,變數的值作為 value,就會有一個很方便的寫法:你只要直接把變數放進 object 就好。

let name = "Mandy";
let phone = "03-0303-3030";

let mom = { name, phone }

console.log(mom.name, mom.phone); //Mandy 03-0303-3030

Object 的 Destructuring 也是一樣的道理喔。如果你要賦值的變數名稱跟 key 相同,那你也不用在打一次

let {name, phone} = {name: 'Mandy', phone: '03-0303-3030'};

應用篇

其實剛剛的用法就已經涵蓋了大概百分之 70 的應用面了。這裡會提到一些 MDN 上面比較有趣的例子,雖然好像自己看也很簡單就是了...

值的互換

最早如果要交換的話可能都要有個第三方的變數來做存放

let a = 3;
let b = 5;
//互換
let c = a;
a = b;
b = c;

console.log(a, b) //5 3

有了解構賦值之後就好棒棒!

let a = 1;
let b = 3;

[a, b] = [b, a];
console.log(a); // 3
console.log(b); // 1

array 裡面也可以這樣用喔

const arr = [1,2,3];
[arr[2], arr[1]] = [arr[1], arr[2]];
console.log(arr); // [1,3,2]

這部分應該算是蠻好理解的

array 的 destructring 忽略中間值會怎麼樣?

如果是下面這樣的話,會出現什麼?

let [a, , b] = [1, 2, 3]

console.log(a, b) //1 3

就是忽略中間的值,蠻直覺的吧

不宣告 object 使用 object 的 destructring

這個我自己也覺得蠻酷的。使用 array 時可以這樣用

let a, b;
[a, b] = [1, 2]

console.log(a, b) //1, 2

先進行宣告,再賦值。

但是如果用在 object 呢?

let a, b;

{a, b} = {a: 1, b: 2} //SyntaxError: Unexpected token '='

會跑出 error 喔,因為宣告 object 的符號 {} 和區分 block 的符號 {} 是一樣的。在這部分會不知道說你要宣告 object 還是要區分出 block。

所以必須在外面加上一個 ( )

let a, b;

({a, b} = {a: 1, b: 2})

console.log(a, b)// 1, 2

這樣一來就沒問題了

巢狀的 destructring

const metadata = {
  title: 'Scratchpad',
  translations: [
    {
      locale: 'de',
      localization_tags: [],
      last_edit: '2014-04-14T08:43:37',
      url: '/de/docs/Tools/Scratchpad',
      title: 'JavaScript-Umgebung'
    }
  ],
  url: '/en-US/docs/Tools/Scratchpad'
};

let {
  title: englishTitle, // rename
  translations: [
    {
       title: localeTitle, // rename
    },
  ],
} = metadata;

簡單說就是包在裡面,這也是沒問題的。

Spread Syntax

function sum (a, b, c) {
  return a + b + c
}

const nums = [1, 2, 3]

console.log(sum(...nums))

spread 可以"展開" 一個 array,這樣講很難理解,更何況後面還會加入 object 的 spread。後來想到一個最好的理解方式。

簡單說,就是把 array 的 [] 去掉。

[1, 2, 3] => 1, 3, 5

看看下面的情境

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

就是把 [2, 4, 5][] 去掉,變成 2, 4, 5 然後放進 [1, 2, 3, 4, 6] 裡面

這個東西可以用在兩個地方:

  1. 放入參數
  2. 分解 array

https://tc39.es/process-document/

Rest Parameters

英文字面上解釋起來就是剩餘參數。會在 function 中使用。可以把剩下的參數,變成一個 array。個人好像比較少用到。其實只是我不會用

基本形式

    function f(a, b, ...theArgs) {
      // ...
    }

這是最基本的形式,來看看範例


    function f(a, b, ...theArgs) {
      console.log("a", a);
      console.log("b", b);
      console.log("theArgs", theArgs);
    }

    f(1, 2, 3, 4, 5)

    //"a" 1
    //"b" 2
    //"theArgs" [3, 4, 5]

    f(1, 2, 3, 4, 5, 6, 7, 8)

    //"a" 1
    //"b" 2
    //"theArgs" [3, 4, 5, 6, 7, 8]

會把剩下的參數都放進去。下面是一點應用建立一個加法的計算機,把所有的參數的 sum up 算出來。

function add(a, b, c) {
    return a + b + c 
}
add (1, 2, 3) //6

但是我們遇到一個問題,這樣只能加三個數字,理想的加法計算機當然是給多少數字都要很豪邁地把它加起來。不管 input 是一個數字還是一百個數字。
像這樣的晴晴我們不確定說會有多少參數,我們就會利用 Rest Parameter 全部放進 array 裡面,這樣不管有幾個參數,都可以使用。

function add(...args) {
   return [args, args.reduce((acc, el) => el + acc, 0 )}
}

add(1, 5, 4) // [[1, 5, 4], 10]
add(1, 5, 4, 2, 1) //[[1, 5, 4, 2, 1], 13]

是不是很棒?當然使用情境也不止這種,簡單來說,他會把剩下的參數都放進一個 array 裡面。不過要注意的是,Rest Parameter 只能放在最後面。

function f(...theArgs, a, b) {
  // ...
}
//請不要以為他會把最後兩個變成 a 跟 b 然後前面的都放進 theArgs

因為這東西跟上面的 Spread Syntax 很像都是 ... ,自己很常搞混。但其實沒那麼複雜,==Rest Parameter 只會在 function 的建構裡面看到她。==

這裡有個比較複雜的範例,綜合了 Destructure 還有 Rest Parameter

function f(...[a, b, c]) {
  return a + b + c;
}

f(1)          // NaN (b and c are undefined)
f(1, 2, 3)    // 6
f(1, 2, 3, 4) // 6 (the fourth parameter is not destructured)

他作了以下事情

function f(...args) { 
// 把所有的參數集中到一個 array,可以想成:

    args = [a, b, c]
    // 然後解構賦值

}

這樣想就單純很多了,只是放在一起看起來牛逼而已。

Arguments.property

很久很久古早以前,有一個 function 內建的 property arguments ,裡面是一個 Object ,內容有 function 中所有的變數。

function test(...args) {
   console.log(arguments)
   console.log(args)

   return args.reduce((acc, el) => el + acc, 0 )
}

/*
[object Arguments] {
  0: 1,
  1: 2,
  2: 3,
  3: 4,
  4: 5
}
[1, 2, 3, 4, 5]
*/

只是一個出來的是 object 另一個是 Array。

Note: If you're writing ES6 compatible code, then rest parameters should be preferred.

不過現在 MDN 是比較推薦使用 rest parameters ,畢竟 array 自帶 map,方便許多,不用再使用 Array.from() 處理一遍。

但這兩個東西還是有差距,讓我們翻到講義第八頁 讓我們看看原文。

  • rest parameters are only the ones that haven't been given a separate name (i.e. formally defined in function expression), while the arguments object contains all arguments passed to the function;
  • the arguments object is not a real array, while rest parameters are Array instances, meaning methods like sort, map, forEach or pop can be applied on it directly;
  • the arguments object has additional functionality specific to itself (like the callee property).
  1. Rest Parameters 只會把剩下的放進去,而不是全部
  2. Arguments 不是 array 他只是一個類 Array (有編號,有 length 的 Object );不過 Rest Parameters 卻是個貨真價實的 Array ,map, forEach 等等的東西應有盡有。
  3. argument 也不是單純的 Object 裡面還有一些 callee 等等的東西。

大概就是這樣,argument 不太好用,而且有雜七雜八的東西,所以才比較堆建 Rest parameter 吧。

結語

這東西也不用刻意去記,看久了自然而然就會使用了,每次在 coding 的時候都多用一點新的語法很快就會記熟了。當初 ES6 甚麼都不懂然後就一腳踏入 React 就直接被炸飛,因為有用到的地方實在太多了,想不懂都不行,而且這部分應該是 ES6 裡面很入門的部分。

Big guy is John。感謝大家的收看~


#frontend #Javscript







Related Posts

module.exports 的運用

module.exports 的運用

解析:純 CSS 的圈圈叉叉

解析:純 CSS 的圈圈叉叉

Leetcode 刷題 pattern - Next Greater Element

Leetcode 刷題 pattern - Next Greater Element


Comments