ES2020から追加された機能について
今回は、javascriptの最新バージョンES2020について、まとめました。- Optional Chaining
- dynamic import
- Promise.allSettled()
- Nullish coalescing Operator
- for in文
- BigInt
- ウェブブラウザの対応状況
nullかundefinedになりうる値へのアクセスが簡単になるOptional Chaining
Optional Chainingとは、構文を用いてnullやundefinedになりうる値へ安全にアクセスできる仕組みです。
構文は、
A?.B Aがnullかundefinedでないとき、Bを返す
利用シーンは、nullやundefinedになるかもしれない値にアクセスしたいときなどです。
例えば、APIからユーザーの住所を取得して出力するプログラムを考えてみましょう。サーバーからは次のようなオブジェクトが返ってくる想定です。
▼ APIから返ってくる想定のオブジェクト
const area = {
name: "東京",
address: {
city: "足立区"
}
};
上記のオブジェクトから cityを出力したいのですが、 データによってはaddressが存在しないかもしれません。従来は、addressの存在を考慮に入れて次のようなコードを記述する必要がありました。
▼ 従来のコード
const city = area.address && area.address.city;
console.log(city);
// 結果は”足立区”
Optional Chainingを使うと、次のようにコードが記述できます。?が Optional Chaining用の演算子です。
▼ Optional Chainingを使った新コード
const city = area.address?.city;
console.log(city);
// addressがない場合はundefinedになる(エラーにならない)。
▼ Optional Chainingの実行結果
cityがなくてもエラーにはならない。
?.構文は何個でも使用できるので、areaもnullになりえるのであれば次のように記述できます。
▼ ?.構文を複数使う例
const city = area?.address?.city;
// 結果: areaもaddressもある場合は「足立区」が出力される。
// それ以外はundefinedになる(エラーにならない)。
DOM要素へquerySelector()を用いてアクセスする場合にも便利に使えます。
▼ HTML
<p>テスト</p>
▼ pタグ内のテキストを出力する例
const paragraphText = document.querySelector("p")?.innerHTML;
console.log(paragraphText);
// 結果: p要素がある場合は、p要素内のテキストを参照して「こんにちは」が出力される。
// それ以外はundefinedになる(エラーにならない)。
dynamic import(動的読み込み)
従来のimport()では、モジュールは即座に読み込まれる仕組みでした。 ですが、dynamic importでは、任意のタイミングでモジュールを読み込むことができることができるようになリました。 例えば、ページの初期表示に必要なJavaScriptだけを読み込んでおき、コンテンツが展開する度に必要なモジュールを遅延ロードすれば、ページの初期表示時の処理負荷が軽減できます。▼ import したいクラス
export class Sub {
subMethod() {
Console.log(“testログ”)
}
}
尚、動作確認はGoogle Chrome、Safari、Firefox、Edgeでのみできます。
▼ 従来のimport
import {Sub} from './sub.js'
const sub = new Sub();
sub.subMethod();
▼ 実行結果
![Elements Console.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/941859/72267f71-79f4-231d-5991-9b247927323c.png) このように、すぐに読み込まれてしまう。▼ dynamic importの場合
import('./sub.js').then(module => {
// 動的に読み込まれたSubクラス
const sub = new module.Sub();
sub.subMethod();
});
resolve()時の引数にモジュールを受け取るため、new module.Sub()とすればsub.js内のSubクラスにアクセスできます。
▼ 動的に読み込まれたSubクラスをコントロールする
setTimeout(() => {
import('./sub.js').then(module => {
// 読み込み完了後にSubを使用する
const sub = new module.Sub();
sub.subMethod();
});
}, 3000);
▼ 3秒後にコンソールするようにセットした結果
![Elements.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/941859/d0b70ea8-3ddd-9da0-2a22-056155f98d51.png) しっかりと、読み込んだモジュールを遅延できている。Promiseの複数処理に便利なPromise.allSettled()
Promise.allSettled()は、複数のPromiseの処理を扱うためのメソッドです。複数のPromiseの1つがrejectされても処理を続行できます。 複数のPromiseを、成功・失敗によらずすべて実行したいとき利用できる。 従来、Promiseの複数同時処理といえば、ES2015で導入されたPromise.all()というメソッドがあります。Promise.all()は複数のPromiseの処理を実行し、全ての処理が成功した場合にはじめて処理が終了するものです。もし、1つでもrejectされるものがあればその時点で処理を終了します。▼ Promise.all()で全ての処理が成功した場合
const promiseList = [
Promise.resolve("成功1"),
Promise.resolve("成功2"),
Promise.resolve("成功3"),
Promise.resolve("成功4")
];
Promise.all(promiseList).then(
resolve => console.log(`resolve: ${resolve}`),
reject => console.log(`reject: ${reject}`)
);
▼ 実行結果
![Console What's New.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/941859/4998dd73-a02b-026d-1011-6b31e2f0407b.png) Promiseの処理が成功のみだと、全ての処理が実行されている次に、途中どれか一つでも、処理が失敗した場合の想定で挙動を見てみる。
期待するコンソールは、成功1、成功2、失敗3、成功4
▼ Promise.all()でrejectされるものがあった場合
const promiseList = [
Promise.resolve("成功1"),
Promise.resolve("成功2"),
Promise.reject("失敗3"),
Promise.resolve("成功4")
];
Promise.all(promiseList).then(
resolve => console.log('resolve: ${resolve}'),z );
▼ 実行結果
![Default levels.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/941859/613e5e31-80c9-dd35-88e6-17e73696ec62.png) 期待どうりの処理がされません。Promise.allSettled()では、rejectされるものがあってもすべての処理が実行されます。したがって、複数の配列のどれかが失敗したとしても処理をすべて実行したい場合に便利です。
▼ Promise.allSettled()でrejectされるものがあった場合
const promiseList = [
Promise.resolve("成功1"),
Promise.resolve("成功2"),
Promise.reject("失敗3"),
Promise.resolve("成功4")
];
Promise.allSettled(promiseList).then(
resolveList => {
console.log("resolve");
for (const resolve of resolveList) {
console.log(resolve);
}
},
reject => {
console.log("reject");
console.log(reject);
}
);
▼ 実行結果
![resolve.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/941859/6475fd5f-1c99-aae7-a0c1-f3ca7223687d.png) Reject、失敗の処理が含まれていますが、すべての処理がされています。nullかundefinedのときだけ値を返せるNullish coalescing Operator
Nullish coalescing Operatorは、「A ?? B」という形で用い、AがnullかundefinedのときだけBを返します。「coalescing」は聞き慣れない単語かもしれませんが、「合体」という意味を持ちます。0、falseなどにfalseに評価されうる値をワンライナーで正しく扱いたい時に使用します。
例えば、次のようなオブジェクトから、foo値を取得して出力する関数を考えた時、もしfooが未設定(nullまたはundefined)ならば、「値なし」という文字列を出力する。
▼ 対象にするオブジェクト
const object1 = {
foo: 100
};
const object2 = {
foo: 200
};
const object3 = {
foo: null
};
const object4 = {
foo: false
};
||(論理OR)演算子を使って、次のようなgetFoo関数を定義するのはNGです。object2、object3が引数として渡された場合にobject.fooがfalseと評価されて「値なし」が出力されるからです。
▼ fooの取得関数のダメな例
function getFoo(object) {
return object.foo || "値なし";
}
console.log(getFoo(object1));
console.log(getFoo(object2));
console.log(getFoo(object3));
console.log(getFoo(object4));
▼ 実行結果
![Console What's New.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/941859/69d06a57-b804-fd65-d196-d00205b07f1e.png) falseも値なしと処理されている。Nullish coalescing Operatorを使えば、object.fooがnullかundefinedの場合のみ「値なし」を返すので、目的の処理をしてくれることになります。
▼ Nullish coalescing Operatorを使った処理
function getFoo(object) {
return object.foo ?? "値なし";
}
console.log(getFoo(object1));
console.log(getFoo(object2));
console.log(getFoo(object3));
console.log(getFoo(object4));
▼ 実行結果
![Console What's New.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/941859/fbbdffde-4fc1-8bb6-5b3e-02a268ac225c.png) falseはしっかりと文字列として処理されている。2^53以上の整数を扱えるBigInt
BigInt は組み込みオブジェクトで、 Number プリミティブで表現できる最大の数、 Number.MAX_SAFE_INTEGER (2^53)よりも大きな数値を信頼できるものとして表現する方法を提供します。 BigInt は任意に巨大な整数に使用することができます。 Number.MAX_SAFE_INTEGER (2^53)以上の値を計算しようとすると、計算結果に誤差が生じます。 その誤差をなくすために使うのが、BigIntです。▼ 計算結果に誤差が生じる例
console.log(9007199254740991 + 2)
期待する値は9007199254740993です。
▼ 実行結果
![Console What's New.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/941859/458bdd7d-34e4-6b25-d702-50b2c1d35526.png) 1のズレが生じてしまう。BitIntを使うと、2^53以上の整数も扱えるようになります。BigIntを扱うには、BigInt(数値)とするか、数値nという記述をします。BigIntはBigInt同士でしか計算ができないので、Numberとあわせて使う場合はNumberもBigIntに変換するようにする。
▼ BigIntの計算例
console.log(BigInt(Number.MAX_SAFE_INTEGER) + 2n);
console.log(9007199254740991n + 2n)
▼ 実行結果
![Console What's New.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/941859/910125af-ec7e-b3dd-0490-d1a45c0d0cfc.png) 今回はしっかりと期待する値が、出ました。 また、出力された9007199254740993nは数学世界での整数9007199254740993を指します。気をつける点として、BigInt はBigDecimal ではないため、演算結果は 0 の方向に丸められます。別の言い方をすれば、小数を返すことはありません。
▼ 小数点が返されない例
const expected = 4n / 2n;
const rounded = 5n / 2n;
console.log(expected);
console.log(rounded)
▼ 実行結果
![Console What's New.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/941859/8a6b43d6-35c3-65e5-9e7f-2cc3af9a096e.png)本来であれば5/2は、2.5ですが、2が返されています。BigIntを使用する際は、このようなことに気をつけること。
for inの順序固定
『for...in文』は『for文』と同じような感じで繰り返し処理を実行できる構文です。『for』文は配列の各要素に対しての繰り返し処理が主な使い方でしたね。それに対して『for...in文』はオブジェクトの各プロパティに対しての繰り返し処理となります。
また、for in はインデックスの順序通りに処理を行うことが保証されていませんでした。
大抵の処理は、インデックス順で処理を行うことが多く、for inの使用は控えられてきました。
var array = {
"hoge": 0,
"hoge1": 1,
"hoge2": 2
};
for(var elem in array){
console.log(elem);
}
従来のfor in分では、この処理の順番が保証されないとすると、これが『0』、『2』、『1』といった順番で出力がされる可能性があったと言うことです。
保証されたと言うことなので、従来のfor in文が処理の順番が、ランダムなのではなく順番がずれる可能性があるよというような状況だってので、実際に、従来のfor in文で順番のズレが生じるのかを、実際に検証することができませんでした。
ウェブブラウザの対応状況
本記事で紹介したES2020の仕様は、次のブラウザで対応しています。2020年2月時点の現行ブラウザで対応しているものには◯、対応していないものは対応開始バージョンを示しています。機能 | Chrome | Firefox | Safari | Edge |
---|---|---|---|---|
Optional Chaining | v80 | v74 | TP 91 | v81 |
dynamic import | ◯ | ◯ | ◯ | ◯ |
Promise.allSettled() | ◯ | ◯ | ◯ | ◯ |
Nullish coalescing Operator | v80 | ◯ | TP 89 | v81 |
for in文 | 不明 | 不明 | 不明 | 不明 |
BigInt | ◯ | ◯ | × | ◯ |
※ SafariのTPはTechnology Previewを示します
ES2020の各機能に対応していないブラウザ、たとえばSafariやEdgeの現行バージョンやIE11なども、TypeScript・Babelとポリフィルを利用すれば対応可能です(BigInt、for...inを除く。import()についてはwebpackも必要)
参考
can i use
最新版TypeScript+webpack 5の環境構築まとめ
最新版で学ぶwebpack 5入門Babel 7でES2020環境の構築