chooblarin’s blog

ES2019を振り返る

November 27, 2019

  • #JavaScript

今年も残すところあと一ヶ月となりましたね。この記事では ES2019 の新機能をユースケースと共に振り返ってみます。

Array.prototype.flat

Array.prototype.flat() は入れ子になった配列の要素を再帰的に辿って結合した配列を新たにつくります。引数には,何階層まで辿るか(depth)指定します。デフォルトは depth = 1 です。

> const array = ["零", ["壱"], [["弐"], [["参"]]]];
> array.flat()
["零", "壱", ["弐"], [["参"]]]

> array.flat(2)
["零", "壱", "弐", ["参"]]

> array.flat(3)
["零", "壱", "弐", "参"]

> array.flat(10) // 多めにdepthを指定
["零", "壱", "弐", "参"]

> array.flat(0) // depth = 0 は何も変更しない
["零", ["壱"], [["弐"], [["参"]]]]

.flat() と同等の効果を得る関数は,.reduce().concat() と再帰処理を組み合わせることで簡単に実装することができます。

const flatten = (array, depth = 1) =>
  array.reduce(
    (acc, val) =>
      acc.concat(
        depth > 1 && Array.isArray(val) ? flatten(val, depth - 1) : val
      ),
    []
  );

Array.prototype.flatMap

Array.prototype.flatMap() は前述の .map().flat() の組み合わせです。すなわち

array.flatMap(func);

array.map(func).flat(1);

は等価です。ここで func は関数で,シグネチャは

(value: T, index: number, array: T[]) => U | Array<U>

です。func が恒等関数 (x) => x のときは.flat() と相当です。すなわち,array.flatMap(x => x)array.flat() は等価です。

> const duplicate = (x) => ([x, x])

> duplicate("👽")
["👽", "👽"]

> ["👽", "👽", "👽"].map(duplicate)
[["👽", "👽"], ["👽", "👽"], ["👽", "👽"]]

> ["👽", "👽", "👽"].flatMap(duplicate)
["👽", "👽", "👽", "👽", "👽", "👽"]

また,.flatMap() を使うと,.map().filter() の効果を同時に得ることができます。

> const list = [
  { name: "Batman" },
  { name: "Jorker", isVillain: true },
  { name: "Gordon" },
  { name: "Alfred" },
  { name: "Harvey" }
];

> list.flatMap(v => v.isVillain ? [] : v.name) // ヴィラン以外の名前一覧が欲しい
["Batman", "Gordon", "Alfred", "Harvey"]

Object.fromEntries()

Object.fromEntries()[key, value] のペアの配列からオブジェクトをつくります。

> const pairs = [["Apple", "🍎"], ["Banana", "🍌"], ["Candy", "🍬"]];
> Object.fromEntries(pairs)
{ "Apple": "🍎", "Banana" :"🍌", "Candy": "🍬" }

key が重複していた場合,配列の後ろ側の値になります。

> Object.fromEntries([
  ["Apple", "🍎"],
  ["Banana", "🍌"],
  ["Candy", "🍬"],
  ["Candy", "🍭"],
  ["Apple", "🍏"]
])
{ "Apple": "🍏", "Banana": "🍌", "Candy": "🍭" }

String.prototype.{trimStart,trimEnd}

.trim() は両端のホワイトスペースを取り除きますが,.trimStart().trimEnd() はそれぞれ,始端,終端のみに作用します。

> "   (^-^)   ".trim()
"(^-^)"

> "   (^-^)   ".trimStart()
"(^-^)   "

> "   (^-^)   ".trimEnd()
"   (^-^)"

Symbol.prototype.description

Symbol を作成するとき,ファクトリ関数に任意の文字列を渡すことが出来ます。

> const symbol = Symbol("これはシンボルです")
> String(symbol)
"Symbol(これはシンボルです)"

これまで,その値を取得するには上記のように文字列に変換するしかありませんでしたが, description という getter でアクセスできるようになりました。

> symbol.description
"これはシンボルです"

Optional catch binding

この提案により,以下のような書き方ができるようになりました。

try {
  valus(); // バルス!
} catch {
  // 何かするが,errorオブジェクトは使わなくていい
}

エラーを揉み消したいとき(推奨はしませんが)や,どんなエラーをキャッチするのか自明なときに便利になります。

let result;
try {
  result = JSON.parse(str);
} catch {
  result = defaultValue;
}

Stable Array.prototype.sort()

Array.prototype.sort() の挙動が安定しました。

> const prices = [
  { name: "Apple", price: 150 },
  { name: "Banana", price: 100 },
  { name: "Broccoli", price: 100 },
  { name: "Corn", price: 120 }
];
> heroes.sort((a, b) => a.price - b.price)

[
  { name: "Banana", price: 100 },
  { name: "Broccoli", price: 100 },
  { name: "Corn", price: 120 },
  { name: "Apple", price: 150 }
]

この例では,アルファベット順にソートされた配列を価格で昇順にソートした例です。これまでは,同じ価格のアイテムがあった場合にその順番がアルファベット順になっていることが保証されていませんでした。この度,.sort() を実行する前の順序が保存されたままソートされるようになりました。

その他

その他,以下のような変更がありました。

  • Well-formed JSON.stringify
  • JSON superset
  • Function.prototype.toString revision

参考