Promiseの組み合わせ
ES2015でPromiseが導入されて以来、JavaScriptでは静的メソッドPromise.all
とPromise.race
の2つのPromiseコンビネーターがサポートされています。
現在、標準化プロセスを進行中の2つの新しい提案があります: Promise.allSettled
とPromise.any
です。この追加により、JavaScriptには合計4つのPromiseコンビネーターが存在し、それぞれ異なるユースケースを可能にします。
ES2015でPromiseが導入されて以来、JavaScriptでは静的メソッドPromise.all
とPromise.race
の2つのPromiseコンビネーターがサポートされています。
現在、標準化プロセスを進行中の2つの新しい提案があります: Promise.allSettled
とPromise.any
です。この追加により、JavaScriptには合計4つのPromiseコンビネーターが存在し、それぞれ異なるユースケースを可能にします。
Array.prototype.flat
この例の配列はいくつかのレベルでネストされています。配列の中にさらに配列があり、その中にも別の配列が含まれます。
const array = [1, [2, [3]]];
// ^^^^^^^^^^^^^ 外側の配列
// ^^^^^^^^ 内側の配列
// ^^^ 最内の配列
Array#flat
は指定された配列のフラット化されたバージョンを返します。
array.flat();
// → [1, 2, [3]]
// …は以下と同じです:
array.flat(1);
// → [1, 2, [3]]
デフォルトの深さは 1
です。ただし、任意の数値を渡して、その深さまで再帰的にフラット化できます。結果にネストされた配列が含まれなくなるまで再帰的にフラット化するには、Infinity
を渡します。
// 配列にネストされた配列がなくなるまで再帰的にフラット化:
array.flat(Infinity);
// → [1, 2, 3]
このメソッドが Array.prototype.flat
と呼ばれる理由で、なぜ Array.prototype.flatten
ではないのかについては、こちらをお読みください: #SmooshGate の詳細を確認!
Array.prototype.flatMap
もうひとつの例を見てみます。duplicate
という関数は引数に値を取り、その値を2回含む配列を返します。この関数を配列のそれぞれの値に適用すると、ネストされた配列が得られます。
const duplicate = (x) => [x, x];
[2, 3, 4].map(duplicate);
// → [[2, 2], [3, 3], [4, 4]]
その後、結果に対して flat
を呼び出して配列をフラット化することができます:
[2, 3, 4].map(duplicate).flat(); // 🐌
// → [2, 2, 3, 3, 4, 4]
このパターンが関数型プログラミングで非常に一般的なので、専用の flatMap
メソッドが登場しました。
[2, 3, 4].flatMap(duplicate); // 🚀
// → [2, 2, 3, 3, 4, 4]
flatMap
は map
を実行し、その後別途 flat
を実行するよりもわずかに効率的です。
flatMap
の使用例について興味がありますか? Axel Rauschmayer の解説をチェックしてください。
Array#{flat,flatMap}
のサポート大きな数値リテラルは、特に繰り返しの数字が多い場合、人間の目で素早く解析するのが困難です。
1000000000000
1019436871.42
可読性を向上させるために、新しいJavaScriptの言語機能で数値リテラルにアンダースコアを区切り文字として使用できるようになりました。その結果、上記のような数値が千単位ごとにグループ化して書き直すことができます。
文字列に対して同じ正規表現を繰り返し適用し、すべての一致を取得することは一般的です。ある程度、String#match
メソッドを使用することでこれを今日行うことは可能です。
この例では、16進数の数字のみで構成されたすべての単語を見つけ、それぞれの一致をログに記録します:
const string = 'Magic hex numbers: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
for (const match of string.match(regex)) {
console.log(match);
}
// 出力:
//
// 'DEADBEEF'
// 'CAFE'
しかし、これでは一致する_サブ文字列_しか得られません。通常、サブ文字列だけではなく、各サブ文字列のインデックスや各一致内のキャプチャグループなどの追加情報も必要です。
独自のループを書き、マッチオブジェクトを自分自身で追跡することでこれを達成することはすでに可能ですが、それは少し面倒であまり便利ではありません:
const string = 'Magic hex numbers: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
let match;
while (match = regex.exec(string)) {
console.log(match);
}
// 出力:
//
// [ 'DEADBEEF', index: 19, input: 'Magic hex numbers: DEADBEEF CAFE' ]
// [ 'CAFE', index: 28, input: 'Magic hex numbers: DEADBEEF CAFE' ]
新しいString#matchAll
APIにより、これまでよりも簡単になりました: シンプルなfor
-of
ループを書くことで、すべてのマッチオブジェクトを取得できます。
const string = 'Magic hex numbers: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
for (const match of string.matchAll(regex)) {
console.log(match);
}
// 出力:
//
// [ 'DEADBEEF', index: 19, input: 'Magic hex numbers: DEADBEEF CAFE' ]
// [ 'CAFE', index: 28, input: 'Magic hex numbers: DEADBEEF CAFE' ]
String#matchAll
は特にキャプチャグループを持つ正規表現に役立ちます。個々の一致ごとの完全な情報を、キャプチャグループを含めて提供します。
const string = 'Favorite GitHub repos: tc39/ecma262 v8/v8.dev';
const regex = /\b(?<owner>[a-z0-9]+)\/(?<repo>[a-z0-9\.]+)\b/g;
for (const match of string.matchAll(regex)) {
console.log(`${match[0]} at ${match.index} with '${match.input}'`);
console.log(`→ owner: ${match.groups.owner}`);
console.log(`→ repo: ${match.groups.repo}`);
}
モダンなWebアプリケーションでは、動的なデータで構成されたリストがよく使用されます。例えば、写真ビューアアプリでは以下のような表示がされることがあります:
この写真にはAda、Edith、_および_Graceが含まれます。
テキストベースのゲームでは、異なる種類のリストが用いられるかもしれません:
あなたの超能力を選んでください:不可視化、念動力、_または_共感力。
各言語ごとにリストのフォーマットの慣例や単語が異なるため、ローカライズされたリストフォーマッタを実装するのは簡単ではありません。サポートしたいすべての言語の単語(上記の例では「and」や「or」など)のリストが必要なだけでなく、それらの言語における特定のフォーマットの慣例もすべてエンコードする必要があります。Unicode CLDRはそのデータを提供していますが、それをJavaScriptで使用するには、他のライブラリコードと一緒に埋め込んで配送する必要があります。このため、そのようなライブラリの場合、バンドルサイズが増加し、読み込み時間、解析/コンパイルコスト、メモリ使用量に悪影響を及ぼします。
JavaScriptモジュールでは、次の構文を使用することがすでに可能でした:
import * as utils from './utils.mjs';
しかし、これまで対称的なexport
構文は存在しませんでした… 今までは:
export * as utils from './utils.mjs';
これは次のものと同等です:
import * as utils from './utils.mjs';
export { utils };
いくつかの提案が既存のJavaScriptクラス構文を新機能で拡張しています。この記事では、V8 v7.2およびChrome 72で導入された新しい公開クラスフィールド構文と、近日公開予定の非公開クラスフィールド構文について説明します。
以下は、IncreasingCounter
という名前のクラスのインスタンスを作成するコード例です:
const counter = new IncreasingCounter();
counter.value;
// ログに'現在の値を取得中!'と出力
// → 0
counter.increment();
counter.value;
// ログに'現在の値を取得中!'と出力
// → 1
注意: value
にアクセスすると、結果を返す前にコード(メッセージをログに出力する)が実行されます。さて、このクラスをJavaScriptでどのように実装しますか?🤔
以下は、ES2015のクラス構文を使用してIncreasingCounter
を実装する方法です:
class IncreasingCounter {
constructor() {
this._count = 0;
}
get value() {
console.log('現在の値を取得中!');
return this._count;
}
increment() {
this._count++;
}
}
このクラスでは、value
ゲッターとincrement
メソッドをプロトタイプに追加しています。さらに興味深いのは、_count
というインスタンスプロパティを作成し、そのデフォルト値を0
に設定するコンストラクタが含まれていることです。現在は、_count
がクラスの利用者によって直接使用されないようにするために、アンダースコアのプレフィックスを使用することが多いですが、これは単なる慣例にすぎず、言語によって特に保護されているわけではありません。
JSON.stringify
は以前、入力に孤立したサロゲートペアが含まれている場合、不正な形式のUnicode文字列を返す仕様でした:
JSON.stringify('\uD800');
// → '"�"'
「適切に形成されたJSON.stringify
」提案では、JSON.stringify
が孤立したサロゲートペアに対してエスケープシーケンスを出力するように変更され、その出力は有効なUnicode(UTF-8で表現可能)となります:
JavaScriptモジュールは現在、すべての主要なブラウザでサポートされています!
この記事では、JSモジュールの使い方、責任を持ってデプロイする方法、そしてChromeチームが将来モジュールをさらに良くするために取り組んでいることについて説明します。
JSモジュール(「ESモジュール」や「ECMAScriptモジュール」とも呼ばれる)は、主要な新機能、または新機能の集合です。過去に独自のJavaScriptモジュールシステムを使用していたことがあるかもしれません。Node.jsのようなCommonJSやAMDなど、もしくは別の何かを使ったかもしれません。これらのモジュールシステムには1つの共通点があります: インポートとエクスポートが可能です。