Open26
JS Primerを読む
主に第一部の基本文法
- 「ECMAScript」はどの実行環境でも共通の仕様、「JavaScript」はそれに実行環境ごとの固有機能が加わる
- strict mode:
"use strict"
。evalやwithが禁止。varやconstなしでの変数宣言禁止 - 実行コンテキスト: script modeとmodule modeがある。多くの実行環境でscriptがデフォルト。strict modeはscript modeではデフォルトoff、module modeではデフォルトon。
- varの問題
-
var x = 1;
のあとにvar x = 2;
と書いてもエラーにならない - 変数巻き上げによる意図しない挙動
-
- プリミティブ型
- Boolean
- Number
- BigInt (ES2020から)
- String
- undefined
- null
- Symbol (ES2015から)
- オブジェクト
- object, array, function, class, regexp, dateなど。プリミティブ型でないもの
-
typeof
すると、オブジェクトはきほん"object"
になる- 例外的に、functionのtypeofは
"function"
- また、
typeof null
は"object"
- 例外的に、functionのtypeofは
- リテラル
- データ型の値を直接記述できるもの。(e.g.,
"hoge"
は文字列リテラル) - 整数リテラル
-
0b10
などで二進数、0o777
などで八進数、0xEEFF
などで十六進数かける。 - また、
0644
みたいに0はじまりの数字を書くと8進数とみなされるが、strict modeではエラーになるので避けるべき
-
- 数値リテラル
- 上記整数リテラルと浮動小数点数リテラルを合わせたもの。最大値は
2^53-1
- 上記整数リテラルと浮動小数点数リテラルを合わせたもの。最大値は
- BigIntリテラル
- 上記最大値を超えられる。
-
9007199254740992n
のように最後にnをつける
- numeric separators
-
1_000_000_000_000
こういう書き方できる。整数、浮動小数点、BigIntのリテラルで使用可能
-
-
undefined
はただのグローバル変数でリテラルではない。(なのでローカルスコープでundefinedという名前の変数を宣言できたりする)
- データ型の値を直接記述できるもの。(e.g.,
- インクリメント演算子(++)
-
num++
は、numを返した後numに1を足す -
++num
は、numに1を足した後にnumを返す
-
- 等価演算子(==)
- 暗黙的型変換をするため避けるべき(===を使う)。ただし、値がnullかundefinedのどちらかであることを判定したい場合のみ、
value == null
とする書き方が例外的に行われる。
- 暗黙的型変換をするため避けるべき(===を使う)。ただし、値がnullかundefinedのどちらかであることを判定したい場合のみ、
- OR演算子(||)
- 左辺がfalsyなら右辺を評価しない(短絡評価)
- NOT演算子(!)
-
!!
で真偽値に変換する方法も見かけるが、多くの場合、より明示的な方法で真偽値を得る方が良い。(e.g.,!!str
よりはstr.length > 0
)
-
- Nullish coalescing演算子(??) ES2020から
- 左辺がnullish (null, undefined)なら右辺を評価する。
- || とかで同様のことをするより意図しない動作のリスクが低い。(e.g., 数値で||でcoalesceしようとすると0の扱いが微妙)
- 暗黙的な型変換は避ける。バグの元
- booleanだと、暗黙的な型変換のルールが少ないため、明示的に変換せずに扱われることも多い(e.g., xがundefinedのとき
if (!x)
とか書く)。ただ、厳密等価演算子で暗黙的型変換を避けた方がベター
- booleanだと、暗黙的な型変換のルールが少ないため、明示的に変換せずに扱われることも多い(e.g., xがundefinedのとき
- 明示的な型変換
-
Boolean(1)
、String(1)
とか。 - シンボルは暗黙的型変換できない。明示的型変換しかできない
-
- 関数の引数
- 関数宣言の引数の個数より多い個数の引数を渡すと、余った奴は無視される
- 少ない個数の引数だと単にundefinedとみなされるがデフォルト値も指定可能
-
f(...args)
などで可変長引数も可能。このとき、...args
のことをrest parameters (残余引数)と呼ぶ- 似たような挙動のものとして、
arguments
という、関数の中でのみ参照できる特殊な変数を使う方法もあるが、arrow functionで使えないなど問題も多いため使わない方がいい
- 似たような挙動のものとして、
- 関数はオブジェクトとして扱える
- ファーストクラスファンクション(第一級関数)と呼ぶ。(c言語とかはこれがなかったりするが、大抵の言語である)
- 関数式
-
const f = function () {...}
みたいなことができる。右辺は無名関数。
-
- arrow function
- 下記特徴
- 名前をつけることができない(常に匿名関数)
- thisが静的に決定できる
- functionキーワードに比べて短く書くことができる
- newできない(コンストラクタ関数ではない)
- arguments変数を参照できない
- 非推奨機能がoffになっていたり、短くかけたりするので、なるべくarrow functionを使うと良い。
- 下記特徴
- メソッド
- objectのプロパティである関数。下記のように書くと見通しが良い。
const obj = { method() { return "this is method"; } };
- 式 (Expression): 値を生成し、変数に代入できるもの
- 文 (Statement): 処理する1ステップ。jsでは文の末尾にセミコロンを置ける。
- 式文 (Expression Statement): 式は文になれる。(逆は無理)。
- ブロック文 (Block Statment):
{}
で囲った部分をブロックと呼ぶ。主にifとかと組み合わせて使う。REPLで、単独のブロック文を使うと、constとかのスコープがその中に限定されるので便利。
- switch
- case節でreturnしてしまうパターンで使うことが多い。
- case節でbreak省略すると、後ろに続くcase節が条件に関係なく実行される。そのためbreakを忘れると意図しない挙動になることがある。breakが頻出する場合、他の方法で書けないか考えると良い。
- while
- 無限ループを生む可能性があるため、他の書き方で代替できる時は避ける
- for...in
- 避ける。オブジェクトのキーに対して反復する時は、
Object.keys(obj).forEach(...)
などを使う。 - for...inは、オブジェクトのプロパティを列挙する際、親オブジェクトまで列挙可能なものがあるかを探索する。これにより意図しない結果になる場合がある。
- 避ける。オブジェクトのキーに対して反復する時は、
- for...of
- これはOK
- iterable (=
Symbol.iterator
というメソッドを実装したオブジェクト)を反復処理できる
- forか配列のメソッド(forEach, map...)か?
- 一長一短。forだとcontinueやbreakをつかえるのがメリット。forEach, map...などは、簡潔にかけることなどメリット。
-
Object
-
Object
はJavaScriptのビルトインオブジェクト。 -
{}
はObject
をもとにして新しいオブジェクトを作成するための構文。(new Object()
と同じ意味)
-
- プロパティアクセス
- 基本的に簡潔なドット記法、それでダメならブラケット記法
- objectのmutability
-
const obj = {}; obj.hoge = 'fuga';
みたいなのはバグを生みやすいので極力避ける -
const obj = Object.freeze({})
のようにObject.freeze
を使うことでプロパティの変更を防げる
-
- プロパティの存在確認
-
obj.key !== undefined
- これだと、「key自体は存在するが、値がundefined」と「そもそもkeyが存在しない」を区別できない
- 上記区別が別に問題なければこれで全然OK
-
"key" in obj
- プロパティが存在したらtrueを返す。OK.
-
Object.hasOwn(obj, "key")
- 2とほぼ同じだが、プロトタイプオブジェクトに関係して、厳密には動作が異なるケースがある
-
obj.hasOwnProperty("key")
- ES2022より前によく使われていた。3と似ているが、プロトタイプオブジェクトに関係する欠点があるため避ける。
-
- オブジェクトの
toString
メソッド-
String
コンストラクタと同じ動作。(String
は、内部でtoString
を呼んでいる)
-
- オブジェクトのプロパティ名は暗黙的に文字列化される
- 数字をプロパティ名にしても文字列になる
- Symbolだけは文字列化されない
-
Object
の静的メソッド- assign
-
const obj = Object.assign(target, ...sources);
で、sourcesに指定された任意の個数のobjectをtargetにマージする。 - spread構文と似てる。(ただし、spread構文は、常に新しいオブジェクトを作成する)
-
- assign
- 全てのオブジェクトは
Object.prototype
プロパティに定義されたprototype
オブジェクトを継承している。-
prototype
オブジェクトは、toString
やvalueOf
など、全てのオブジェクトから利用できるメソッドなどを提供するベースオブジェクト - これらのメソッドをプロトタイプメソッドという。
- あらゆるオブジェクトのインスタンスから、プロトタイプメソッドを参照できる仕組みをプロトタイプチェーンと呼ぶ
-
-
Object.hasOwn
とin
の違い-
in
はプロトタイプメソッドまで探索。hasOwn
はそのオブジェクト自体のプロパティのみ探索。 - つまり、
Object.hasOwn(obj, "toString")
はfalse、"toString" in obj
はtrue
-
-
Object.hasOwn
とobj.hasOwnProperty
の違い- 前者は、プロトタイプメソッドが使えなくてもOK
-
Array
のインスタンスもオブジェクト。なのでプロトタイプメソッドが使える。 -
const obj = Object.create(null);
で、Object.prototype
を継承しないオブジェクトができる。これはMapの代わりとかに使われるケースがあったが、今は使う理由ない
- 疎な配列:
[1, ,3]
みたいなやつ。2番目の要素がundefined。密な配列には全部に値が入ってる。疎な配列で、インデックスは存在するが値はないケースと、そもそもインデックスがないケースを区別するには、Object.hasOwn(arr, 1)
などを使う。 - at:
arr.at(0)
とかでarr[0]
と同じことができる。また、**arr.at(-1)
で最後の要素にアクセスできる。**神 - flat: 多次元配列を1次元に変換する。flatの引数に、フラット化する深さを指定。
Infinity
を渡すと、任意の多次元配列を1次元にする。(e.g.,[["A"], "B"].flat(Infinity)
) - 追加・削除: 配列の頭: shift(削除), unshift(挿入)。配列の最後: pop(削除), push(挿入)。配列の途中: splice(削除、追加)
- 破壊的メソッド: pop, push, splice, reverse, shift, sort, unshift, copyWithin, fill
- 配列に対してループを回して配列以外のoutputを得たい時
- reduceだとlet使わなくていいが、可読性悪い
- forEachだとlet必要だが、可読性良い
- 一長一短。reduceの可読性は、reduceしてる処理を関数でラップするなどするとよい
- 配列操作のメソッドチェーン: 長すぎるチェーンは読みにくいが、適度なチェーンは処理をスッキリさせるため良い
- ECMAScriptは文字コードとしてUnicodeを採用、エンコード方式としてUTF-16を採用。Unicodeでは1文字を表すのに使う最小限のビットの組み合わせをCode Unitと呼び、UTF-16では各Code Unitのサイズが16ビット(2バイト)。
- JavaScriptにおける文字列は16ビットのCode Unitが順番に並んだものとして内部的に管理される。
- lengthメソッドは、文字列のCode Unitの個数を返す
- 文字列の一部を取得
-
slice
とsubstring
の2種類。ほぼ同じだが、substring
の場合引数にマイナスを指定すると常に0として扱われるという違いがある。どちらを使うかは好み。
-
- 検索
-
indexOf
とlastIndexOf
-
startsWith
、endsWith
、includes
- 正規表現
-
RegExp
と正規表現リテラル(/.../
)。リテラルの場合は、動的に値を変更できないが、JSのパースの時に正規表現が誤っていたらエラーを出してくれる。RegExp
の場合は、動的に中身を変更できる。正規表現が静的で良い時はなるべくリテラルを使うと良い。 -
str.search(/\d{3}/)
などで正規表現検索し、一致した文字列のインデックスを返す。 -
str.match(/\d{3}/)
などで正規表現検索し、一致した文字列とインデックスを返す。正規表現にg
がついている場合は、マッチした全ての結果を含んだ配列を返す。 -
str.matchAll(/\d{3}/)
などで正規表現検索し、マッチした結果をiteratorで取得する。RegExpのexec
と似ているが、こちらの方が使いやすいので基本的にmatchAll
を使えば良い。 - 全般、正規表現は柔軟性高いが可読性が低いため、必要なところだけ使う。また、適宜コメントを添えると良い。
-
-
- 削除・置換
-
replace
で一つだけ置換 -
replaceAll
で全てを置換 -
replace
にはコールバック関数を渡すこともできる。下記のようなことができる
dateString.replace(/(\d{4})-(\d{2})-(\d{2})/g, (all, year, month, day) => `${year}年${month}月${day}日`)
-
- 連結
-
+
で連結できるが、URL, pathなど専用のモジュールがある場合はそちらを優先的に使うべき
-
- タグ付きテンプレート
-
関数`テンプレート`
という形でタグ付きテンプレートが記述できる。下記のような動作をする。
// 呼び出し方によって受け取る引数の形式が変わる function tag(strings, ...values) { // stringsは文字列のパーツが${}で区切られた配列となる console.log(strings); // => ["template "," literal ",""] // valuesには${}の評価値が順番に入る console.log(values); // => [0, 1] } // ()をつけずにテンプレートを呼び出す tag`template ${0} literal ${1}`;
-
- code point
- Unicodeにおける、文字に対する一意のIDのこと。
str.condePointAt(インデックス)
で、文字列の特定の位置の文字のcode pointを取得可能 - 文字列の中に、
\u{Code Pointの16進数の値}
などとcode pointの値を埋め込むと、それに対応する文字が出力される
- Unicodeにおける、文字に対する一意のIDのこと。
- code pointとcode unit
- 大体code pointとcode unitは同じ。
- ただ、絵文字とかはcode pointとcode unitが一致しなくなる。絵文字1つに対して、2つのcode unitが対応する。
- code pointの数(=unicodeの文字の種類数)は10万種類を超えているが、code unitは65536種類しか表現できないため、こういうことが生じる。
- 2つのcode unitの組み合わせ(=4バイト)で1つのcode pointを表現する仕組みをサロゲートペアという。
- code point単位で文字列を扱う
-
str.length
とかは、code unit単位で文字列を扱うので、「1文字だけどlengthは2」というのが生じる - 下記は、文字列をcode pointごとに扱ってくれる
-
CodePoint
を名前に含むメソッド -
u
(Unicode)フラグが有効化されている正規表現。(e.g.,/(.)のひらき/u
。uを正規表現につけて問題になるケースは少ないはず) - 文字列のiterator (
for...of
、Array.from
など)
-
-
- プリミティブ型のうち、boolean, number, bigint, string, symbolには、それぞれ対応するオブジェクトが存在する。
- 例えば、
String
はstring型に対応するオブジェクト。 - こういうのをラッパーオブジェクトという。
- 例えば、
new String(str)
でラッパーオブジェクト作れる。(これに対してtypeofするとobjectになる)
- 例えば、
- プリミティブ型に対してプロパティアクセスする(e.g.,
str.toUpperCase()
)と、自動的にラッパーオブジェクトへの変換が行われる。 - 基本的に、
new String(str)
する必要はない。必要な時には自動的に変換が行われるし、typeof
をつかったときにobjectになってしまう混乱があるため。
- 関数スコープ
- 関数の中のスコープ。(関数内で定義された変数、仮引数)
- ブロックスコープ
- ブロック内で宣言された変数
- for文は、ループごとに新しいブロックスコープを作成
- スコープチェーン
- ブロックを入れ子した時、内側のスコープからは外側のスコープの変数を参照可能。内側から外側に変数を探していく仕組みをスコープチェーンと呼ぶ
- グローバルスコープ
- 一番外側のスコープ。プログラム実行時に暗黙的に作成。
- グローバル変数には、自分で定義した変数と、ビルトインオブジェクト(e.g.,
undefined
、isNaN
、document
)がある
- var宣言の巻き上げ(hoisting)
- varは、宣言前に参照できる(letとかは不可)
- 動作イメージとしては、変数の宣言部分がもっとも近い関数またはグローバルスコープの先頭に移動するような感じ。
- これによりvarの動作は予測しにくい。varはなるべく使わないようにする。
- functionによる関数宣言の巻き上げ
- varと同様、functionで宣言された関数も宣言前に参照できる。
- varと異なり、これが問題になることはほとんどない。
- クロージャー
- 「外側のスコープにある変数への参照を保持できる」という関数が持つ性質により、あたかも関数が状態を持っているような状態を実現すること。
- 下記のようなコード
// `increment`関数を定義して返す関数 function createCounter() { let count = 0; // `increment`関数は`count`変数を参照 function increment() { count = count + 1; return count; } return increment; } // `myCounter`は`createCounter`が返した関数を参照 const myCounter = createCounter(); myCounter(); // => 1 myCounter(); // => 2
- 静的スコープ(レキシカルスコープ)
- jsのスコープは、コード実行前に決定する。(=どの識別子がどの変数を参照しているかが静的に決定される)
- メモリ管理
- 基本的に、どこからも参照されなくなったデータはGCされる
- 「関数の中で定義した変数は、関数の実行が終了したらGCされる」は間違い。返り値の中でその変数を参照していたら、GCされない
- クロージャーは、上記静的スコープとGCの仕組みを利用して動いている。
- クロージャーの用途
- 関数に状態を持たせる
- グローバル変数を減らす
- 高階関数の一部として。下記のようなケース
function greaterThan(n) { return function(m) { return m > n; }; } // 5より大きな値かを判定する関数を作成する const greaterThan5 = greaterThan(5); console.log(greaterThan5(4)); // => false console.log(greaterThan5(5)); // => false console.log(greaterThan5(6)); // => true
- 実行コンテキストと
this
- script mode: トップレベルスコープにおいてthis = グローバルオブジェクト。(e.g., ブラウザならwindow)
- module mode: トップレベルスコープにおいてthis = undefined。(グローバルオブジェクトを参照したい場合は
globalThis
を使う)
- arrow function以外の関数とメソッドにおける
this
-
this
は実行時に暗黙的に渡される引数のようなもの。実行時に値が決まる。 -
this
の参照先はベースオブジェクト。obj.method()
のように関数を呼び出した時、obj
がthis
となる。相当するベースオブジェクトがない場合はundefinedになる。(なお、strict modeじゃない場合は、thisがundefinedの場合、自動的にグローバルオブジェクトに変換されてしまう) -
this
が問題になるパターン-
const fn = obj.method
のようにメソッドを他の変数に代入しなおすと、fn
を呼んだときのthisはundefinedになってしまう。 - 対処法
- メソッドを変数に入れ直さない(推奨)
-
call
,apply
,bind
などを使ってthis
を明示する-
関数.call(thisの値, ...関数の引数)
や関数.apply(thisの値, [関数の引数1, 関数の引数2])
によりthis
を明示して関数を呼べる -
関数.bind(thisの値, ...関数の引数);
とすると、this
と引数を固定した新しい関数ができる
-
- mapとかに渡すコールバックの中で
this
を参照すると、そのmapをメソッドの中で使っていたとしても常にundefinedになってしまう
-
-
- arrow functionの関数とメソッドにおける
this
- arrow functionでは
this
は暗黙的な引数ではない。常に関数の外側のスコープのthis
を探しにいく。 - つまり、Arrow Functionにおけるthisは「Arrow Function自身の外側のスコープに定義されたもっとも近い関数のthisの値」となる。
- 例えば下記
const Prefixer = { prefix: "pre", prefixArray(strings) { return strings.map((str) => { // `Prefixer.prefixArray()` と呼び出されたとき // `this`は常に`Prefixer`を参照する return this.prefix + "-" + str; }); } };
- arrow functionで定義した関数に対して
call
,apply
,bind
を使ってthis
を固定しようとしても無視される。
- arrow functionでは
-
this
はOOPの文脈で導入された。 メソッド以外においてもthisは評価できるが、strict modeや実行コンテキストに依存するためバグの元。 使うのはメソッドの中のみにすると良い。
- class構文で定義したクラスは関数オブジェクトの一種。
- プロトタイプメソッド
-
class クラス { メソッド() {...} }
みたいな形のメソッド - このメソッドは、クラスの各インスタンスかから共有される(つまり、
instanceA.method === instanceB.method
。ただし、メソッド内で参照するthis
はインスタンスごとに異なる)。
-
- アクセッサプロパティ
- getter, setter
-
get プロパティ名()
とかset プロパティ名(仮引数)
- getter, setterの中に追加処理を織り込むことで、「あたかもプロパティを読み書きしてるだけなのに追加処理が走る」みたいなのが実現できる。(e.g., SWRのdependency collection)
- Privateクラスフィールド
- 下記のように先頭に#をつけたクラスフィールドは外から読み書きできない。
class クラス { // プライベートなプロパティは#をつける #フィールド名 = プロパティの初期値; }
- publicクラスフィールドは、フィールドの宣言が必須ではないが、privateクラスフィールドは必須。
- staticメソッド, クラスフィールド
- 先頭にstaticつけるだけ
- プロトタイプメソッドとインスタンスに定義したメソッド
- 下記のように2通りの書き方でメソッド定義できる。
class ExampleClass { // クラスフィールドを使い、インスタンスにメソッドを定義 instanceMethod = () => { console.log("インスタンスメソッド"); }; // メソッド構文を使い、プロトタイプオブジェクトにメソッドを定義 prototypeMethod() { console.log("プロトタイプメソッド"); } } const example = new ExampleClass();
- このとき、プロトタイプメソッドはプロトタイプオブジェクトに定義、インスタンスメソッドはインスタンスに定義されている。
- なので、
ExampleClass.prototype.prototypeMethod
が存在する。 - ただし、インスタンスからはプロトタイプオブジェクトを直接参照できない。
Object.getPrototypeOf(インスタンス)
で参照できる。 -
クラス.prototype === Object.getPrototypeOf(インスタンス)
となる。
- なので、
- プロトタイプメソッドはインスタンスそのものには定義されていないが、プロトタイプチェーンの仕組みにより、問題なくインスタンスから呼べる
- 継承
-
Array
、String
などビルトインオブジェクトも継承できる
-
- throwする時は
Error
オブジェクトのインスタンスを投げること。そうしないとスタックトレースが得られない - ビルトインエラー
-
Error
オブジェクトを継承した、エラー状況に応じたエラーがある(e.g.,ReferenceError
,SyntaxError
)
-
-
console.error
はスタックトレース出してくれる。エラーログはこちらに寄せるとよい
- 基本的にJSの非同期処理は並行処理。(web workerとかを除く)
- Promise
- 下記のように使う
// asyncPromiseTask関数は、Promiseインスタンスを返す function asyncPromiseTask() { return new Promise((resolve, reject) => { // さまざまな非同期処理を行う // 非同期処理に成功した場合は、resolveを呼ぶ // 非同期処理に失敗した場合は、rejectを呼ぶ }); } // asyncPromiseTask関数の非同期処理が成功した時、失敗した時に呼ばれ る処理をコールバック関数として登録する asyncPromiseTask().then(()=> { // 非同期処理が成功したときの処理 }).catch(() => { // 非同期処理が失敗したときの処理 });
-
Promise
インスタンスは、非同期処理が成功したか失敗したかを表すオブジェクト。これに対して、then
やcatch
で成功時・失敗時に呼び出される処理をコールバックとして登録することで非同期処理の結果を扱う。 -
then
に二つ引数を渡すこともできる。promise.then(onFulfilled, onRejected)
みたいな。このとき、失敗時にはonRejectedの方が呼ばれる。catch
は実質、then(undefined, onRejected)
と同じ - Promiseの状態
- Fulfilled (resolveが呼ばれた), Rejected (rejectが呼ばれたまたは例外発生), Pending (どちらでもない、初期状態)の3種類
- これらを直接Promiseインスタンスから取り出す方法はない
- Fulfilled, RejectedをまとめてSettledと呼ぶ。Settledになったpromiseはその後別の状態には変化しない。(=resolveした後にrejectやresolveを何度呼んでもFulfilledのまま)
-
Promise.resolve(42)
などとすると、new Promise((resolve) => resolve(42))
と同じ意味になる。(糖衣構文)。Promise.reject
も同様。
- Promiseチェーン
-
then
やcatch
は新しいPromiseインスタンスを生成するため、チェーン状に処理を繋げられる。 - このチェーンでは、Promiseの状態がRejectedとなった場合には、最も近いcatch (もしくはthenの第二引数)の処理が実行され、それまでのthenはスキップされる。一度catchされたら、そのあとのチェーンでは成功時の処理(thenの第一引数)が実行される。
- Promiseチェーンのコールバック関数では、基本的に値を返すとresolveしたものとみなされる。ただし、コールバックの中で
return Promise.reject(...)
などPromiseインスタンスを返すこともできる。これにより、明示的にコールバックの実行が失敗したか成功したかを示せる - Promiseチェーンの最後に
finally(...)
もかける
-
-
Promise.all
はPromiseインスタンスの配列を受け取り、新しいPromiseインスタンスを返す。これは、全部resolveだったらFulfilledになる。Promise.race
は似ているが、配列の中で一番最初にSettledになった時点でSettleし、次の処理を実行する
- Async function
- 常にPromiseインスタンスを返す
- 値をreturnした場合: その返り値のFulfilledなPromiseを返す
- Promiseをreturnした場合: そのPromiseをそのまま返す
- 例外発生: そのエラーのRejectedなPromiseを返す
- await
- awaitの右辺のPromiseインスタンスがFulfilledとなった場合は、resolveされた値を返す。Rejectedとなった場合は、エラーをthrowする
- ES2022からは実行コンテキストがmoduleだったらトップレベルでawaitかける
- 常にPromiseインスタンスを返す
- ObjectをMapとして使うデメリット
- プロトタイプオブジェクトから継承されたプロパティ(e.g, constructor)は使えない
- これは
Object.create(null)
することで回避できるが、今はMap使えば良い
- これは
- キーとして文字列かSymbolしか使えない
- プロトタイプオブジェクトから継承されたプロパティ(e.g, constructor)は使えない
- ObjectをMapとして使うメリット
- リテラル表現(
{}
)があるため作成しやすい -
JSON.stringify
できる- fetch APIのbodyとしてマップを渡したりするときなど
- 多くのライブラリやネイティブAPIで、マップとしてObjectが渡される設計になっている
- リテラル表現(
- WeakMap
- キーがオブジェクトのとき、そのオブジェクトが参照されなくなったら、キーに存在していても自動でGCしてくれる
- iterableではないのでkeysやsizeなどは使えない
- Set
- iterable。forEach使える。entriesやkeysも使えるが、keyに相当する部分には単に値が入る。
- 反復処理の順番は要素の挿入順。
- WeakSetもある
-
JSON.stringify
の引数- 第二: replacer。関数を渡した場合、文字列変換の際の挙動をコントロールできる。配列を渡した場合、プロパティの許可リストになる。
- 第三: space。変換後の文字列を読みやすくフォーマットする際のインデント。
- 自作クラスに
toJSON
メソッドを入れておくと、JSON.stringify
したときにそれを使ってくれる
-
toISOString
でISO 8601形式に - Dateコンストラクタには、ミリ秒・文字列・時刻の部分を分割したもの の3種類が渡せる
- ミリ秒はタイムゾーン問題ないため安全
- 文字列はISO 8601やRFC2822の形式が渡せる。タイムゾーン未指定の場合は実行環境のタイムゾーンに
-
new Date(year, month, day, hour, minutes, seconds, milliseconds)
の形式でインスタンス化する場合は、常に実行環境のタイムゾーンになる。これが嫌な場合はDate.UTC
でインスタンス作ると良い
- 多くの場合、機能が不十分なのでday.jsとかdate-fnsを使う
- ESモジュールはexport、import文が使える。named export/importとdefault export/import。
- default import/exportは、
default
という固有の名前による名前付きエクスポートと同じもの。- なので、
const foo = 'foo'; export { foo as default }
でデフォルトエクスポートできるし、import { default as foo } from './foo.js'
でデフォルトインポートできる
- なので、
- ESモジュールはブラウザでは下記のようにつかう
<!-- my-module.jsをECMAScriptモジュールとして読み込む -->
<script type="module" src="./my-module.js"></script>
<!-- インラインでも同じ -->
<script type="module">
import { foo } from "./my-module.js";
</script>
- Ecma International: ECMAScript, C#, Dartなどの標準化を行う
- この中のTechnial Committee 39 (TC39)がECMAScript仕様を議論
- ECMAScriptの仕様書のドラフトは https://github.com/tc39/ecma262 で管理、日々更新。(living standard)
- 日々更新されるものの、毎年バージョン番号をつけたもの(e.g., ES2017)も公開される。これはスナップショットのようなもの。
- ブラウザなどに実装する際は、最新のECMAScriptを参照している
- 仕様策定では、機能ごとにプロポーザルが出される。プロポーザルにはステージ0-4がある。4になると仕様書ドラフトにマージされる。