Blog icon indicating copy to clipboard operation
Blog copied to clipboard

[MDN] Destructuring assignment

Open ChaoLiou opened this issue 4 years ago • 0 comments

Destructuring assignment

  • 他的語法是一種 JavaScript expression, 可以直接
    • [] 取出項目值,
    • {} 取出屬性值,
  • 再放到獨立的變數

語法

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

[a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(a); // 10
console.log(b); // 20
console.log(rest); // [30, 40, 50]

({ a, b } = { a: 10, b: 20 });
console.log(a); // 10
console.log(b); // 20

({ a, b, ...rest } = { a: 10, b: 20, c: 30, d: 40 });
console.log(a); // 10
console.log(b); // 20
console.log(rest); // {c: 30, d: 40}

說明

  • []{} 目前有一種 literal expression 的寫法, 建立臨時資料.
const x = [1, 2, 3, 4, 5];
// const x = { a: 1, b: 2, c: 3 };
  • 使用相似語法, 會在 = 的左側定義好要取出右側變數的甚麼值.
const x = [1, 2, 3, 4, 5];
const [a, b] = x;
console.log(a); // 1
console.log(b); // 2
const x = { a: 1, b: 2, c: 3 };
const { a, b } = x;
console.log(a); // 1
console.log(b); // 2

範例

陣列

基本

const hexes = ["#ff0000", "#ffff00", "#008000"];

const [red, yellow, green] = hexes;
console.log(red); // "#ff0000"
console.log(yellow); // "#ffff00"
console.log(green); // "#008000"

使用宣告好的變數

let a, b;

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

預設值

  • 如果取出的值是 undefined, 則使用預設值.
let a, b;

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

交換變數值

  • 過去我們要交換兩個變數的值, 需要一個暫存變數.
let a = 1;
let b = 3;

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

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

用在函式回傳值

function f() {
  return [1, 2];
}

let a, b;
[a, b] = f();
console.log(a); // 1
console.log(b); // 2

忽略你不要的回傳值

function f() {
  return [1, 2, 3];
}

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

const [c] = f();
console.log(c); // 1
  • 忽略所有回傳值:
[, ,] = f();

用於陣列剩餘的項目

  • 可以將陣列剩餘的項目取出來成為一個陣列:
const [a, ...b] = [1, 2, 3];
console.log(a); // 1
console.log(b); // [2, 3]
const [a, ...b,] = [1, 2, 3];

// SyntaxError: `...的項目` 不可以接逗號
// 因為 `...的項目` 後面不會有任何項目

將正規表示的結果取出

  • 當正規表示法 exec() 找到符合後, 會回傳一個陣列.
    • 第一個項目是完全符合的字串,
    • 接著的項目是每組小括號的符合字串
function parseProtocol(url) {
  const parsedURL = /^(\w+)\:\/\/([^\/]+)\/(.*)$/.exec(url);
  if (!parsedURL) {
    return false;
  }
  console.log(parsedURL);
  // ["https://developer.mozilla.org/en-US/docs/Web/JavaScript",
      "https", "developer.mozilla.org", "en-US/docs/Web/JavaScript"]

  const [, protocol, fullhost, fullpath] = parsedURL;
  return protocol;
}

console.log(parseProtocol('https://developer.mozilla.org/en-US/docs/Web/JavaScript'));
// "https"

物件

基本

const user = {
  id: 42,
  is_verified: true,
};

const { id, is_verified } = user;

console.log(id); // 42
console.log(is_verified); // true

使用宣告好的變數

let a, b;

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

注意: 用於 object literal, 且沒有宣告, 你需要加上小括號 (...).

{a, b} = {a: 1, b: 2} 不合法, 因為左測的 {a, b} 視為 block, 而不是 object literal.

({a, b} = {a: 1, b: 2}) 合法, 因為等同於 let {a, b} = {a: 1, b: 2}

(...) 的前面需要一個分號, 不然可能會把前一行當作函式執行.

使用新的變數名稱

const o = { p: 42, q: true };
const { p: foo, q: bar } = o;

console.log(foo); // 42
console.log(bar); // true
  • 舉例, const {p: foo} = o 是從物件 o 取出一個叫做 p 的屬性, 再給值到一個叫做 foo 的區域變數.

預設值

  • 如果取出的值是 undefined, 則使用預設值.
const { a = 10, b = 5 } = { a: 3 };

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

使用新的變數名稱, 且提供預設值

  • 可以同時
    • 從物件取出屬性, 再給值到一個不同名稱的變數
    • 如果取出的是 undefined, 使用預設值
const { a: aa = 10, b: bb = 5 } = { a: 3 };

console.log(aa); // 3
console.log(bb); // 5

直接將物件傳入函式的設計

const user = {
  age: 30,
  displayName: "王阿明",
  fullName: {
    firstName: "小明",
    lastName: "王",
  },
};

function userAge({ age }) {
  return age;
}

function whois({ displayName, fullName: { firstName: name } }) {
  return `${displayName} 就是 ${name}`;
}

console.log(userAge(user)); // 30
console.log(whois(user)); // "王阿明 就是 小明"

函式參數的預設值

function drawChart({ size = "大", coords = { x: 0, y: 0 }, radius = 25 } = {}) {
  console.log(size, coords, radius);
}

drawChart({
  coords: { x: 18, y: 30 },
  radius: 30,
});
// "大", { x: 18, y: 30 }, 30

drawChart();
// "大", { x: 0, y: 0 }, 25

如果你想要在呼叫函式時, 不用提供任何參數, 目前的設計很適合.

用於巢狀的物件和陣列

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, // 取 title 值, 再給值到 englishTitle
  translations: [
    {
      title: localeTitle, // 取 title 值, 再給值到 localeTitle
    },
  ],
} = metadata;

console.log(englishTitle); // "Scratchpad"
console.log(localeTitle); // "JavaScript-Umgebung"

用於 for-of

const people = [
  {
    name: "Mike Smith",
    family: {
      mother: "Jane Smith",
      father: "Harry Smith",
      sister: "Samantha Smith",
    },
  },
  {
    name: "Tom Jones",
    family: {
      mother: "Norah Jones",
      father: "Richard Jones",
      brother: "Howard Jones",
    },
  },
];

for (const {
  name: n,
  family: { father: f },
} of people) {
  console.log("名字是: " + n + ", 父親是: " + f);
}

// "名字是: Mike Smith, 父親是: Harry Smith"
// "名字是: Tom Jones, 父親是: Richard Jones"

用於經過運算的屬性名稱

let key = "name";
let { [key]: myName } = { name: "王小明" };
// 如同 let { name: myName } = { name: "王小明" };

console.log(myName); // "王小明"

用於 rest 語法

  • 可以將物件剩餘的屬性取出來成為一個物件.
let { a, b, ...rest } = { a: 10, b: 20, c: 30, d: 40 };
a; // 10
b; // 20
rest; // { c: 30, d: 40 }

用於不合法的變數名稱

const user = { "is-taiwanese": true };

const { "is-taiwanese": isTaiwanese } = user;
// 因為沒辦法寫成 const { is-taiwanese } = user;

console.log(isTaiwanese); // "true"

用於陣列和物件

  • 你可以組合使用. 假設要 prop 的第三個項目, 且該項目物件的 name:
const props = [
  { id: 1, name: "Fizz" },
  { id: 2, name: "Buzz" },
  { id: 3, name: "FizzBuzz" },
];

const [, , { name }] = props;

console.log(name); // "FizzBuzz"

用於物件時, 也可能會取出其 prototype 物件中的屬性

let obj = { self: "123" };
obj.__proto__.prot = "456";
const { self, prot } = obj;
// self "123"
// prot "456" (從 prototype 取出)

瀏覽器相容

  • 只有 IE 不支援, 其他瀏覽器都完全支援

ChaoLiou avatar Jan 22 '21 07:01 ChaoLiou