【技術書まとめ】『JavaScript Primer』を読んだまとめ

## 第一部: 基本文法

### データ型とリテラル

- `typeof null` は `object` となる
- `""`と`''`は全く同じ
- 複数行の文字を入れるなら`
- `undefined`はただのグローバル変数で、`undefined`という値を持っているだけ
    - リテラルではない

### 演算子

- JSでは`10 + 0.5`は`10.5`となる
    - `> 数値は内部的にIEEE 754方式の浮動小数点数として表現されています `
- `NaN === NaN`は`false`になる
- `==`は使うべきではない
    - 暗黙的な型変換をするから
        - `1 + true; // => 2`
        - 使ってよい例外: `value == null`でnullとundefindを両方比較するとき
            - しかし意図が読めないときもある
- `indexOf`は見つからないとき`-1`を返す
    - `if (~str.indexOf("木"))`で 0 となる
        - 0 は false なので実行されない
- `includes("木")`で見つけられる
- `""`（空文字列）は`false`


### 暗黙的な型変換

- `1 + "2"; // => "12"`
- `Number.parseInt("1", 10);`で1をパースして10進数で取り出す
    - 文字列とundefindは`NaN`となる
        - `Number.isNaN(NaN);`でNaNとわかる
- sumでundefinedが入ると"NaN"になる
    - `if (typeof value !== "number")`で防いだりする
- 空文字列は`typeof str === "string" && str.length === 0;`で判定する
- `const obj1 = { foo: 'bar' }; const obj2 = { foo: 'bar' }; console.log(obj1 === obj2);`
    - falseになる
        - 別々に作ったら別のオブジェクトだから



### 関数と宣言

- 仮引数より呼び出し時の引数が少ない時、余ったものは`undefined`になる
    - 溢れたときは無視される
- `prefix || 'デフォルト';`だと空文字列の時にもデフォルトが入ってしまう
    - デフォルト引数にする
    - `prefix ?? 'デフォルト';`にする
- まとめて入れたり、残りをまとめたり、まとめて出したりは`...array`でできる
    - Rest parameters
- 関数の中でのみ使える `arguments[0]`
    - `function() { console.log(arguments[0]); }`のように引数が定義されてなくても使える
        - Rest parameters が使えるならこれは使わない方がいい
- `function printUserId({ id }) { ... }`で`user.id`のidだけとれる
    - `const { id } = user;`でもとれる
        - 分割代入
- 関数は関数オブジェクト
- アロー関数
    - 常に名前がない
    - `this`が決まっている
    - 短く書ける
    - `new`できない
    - `argument`変数は使えない
- 同じ名前の関数宣言は上書きされる
    - `function`と`var`を使ったときだけ起こる
- 引数となる関数は***コールバック関数***とよぶ
    - コールバック関数を使う関数やメソッドを***高階関数***とよぶ
- JSでは関数とメソッドの違いはあまりない


### 文と式

- 式
    - 値を生成し、変数に代入できるもの
- 文
    - if文など
- ブロックで終わる文にはセミコロンは不要
    - 匿名関数は式だからセミコロンは必要

### 条件分岐

- falsyな値
    - `false`
    - `undefined`
    - `null`
    - `0`
    - `0n`
    - `NaN`
    - `""`（空文字列）
- falsyな値以外は真偽値に変換すると`true`となる


### ループと反復処理

- `some`は一度でも`true`が返ってくると反復処理を終了する
    - `numbers.some(isEven);`
- `filter`は`true`になった値だけを集められる
    - `array.filter(isEven);`
- `reduce`で反復処理ができる
    - `array.reduce((前回の値, 現在の値) => { return 次の値; }, 初期値);`
        - `numbers.reduce((total, num) => { return total + num }, 0);`


### オブジェクト

- オブジェクトとはプロパティの集合
    - プロバティはキーとバリューの対
- プロパティ名と変数名が同じだと`{ name }`と省略して書ける
    - importの時などに使う
- プロパティ名に変数を使うときは`[]`を使う
    - `languages[myLang]`
- オブジェクトのプロパティを変数として使うときは分割代入する
    - `const { ja, en } = languages;`
- プロパティの削除は`delete obj.key1`
- JSの`const`は再代入を防ぐだけ
    - 値の変更はできてしまう
        - `const obj = { key: "value" };`でも`obj.key = "Hi!";`できる
    - 変更防止は`Object.freeze`を使う
- 存在しないプロパティにアクセスすると`undefined`となる
    - 例外は発生しない
    - プロパティを持つか確認するには？
        - `undefined`と比較する
            - `obj.key !== undefined`
                - 値が`undefined`のときと区別できない
                    - 最終的に取得したいのが値の時に使う
        - `in`演算子
            - `if ("key" in obj) { ... };`
        - `hasOwnProperty`メソッド
            - `if (obj.hasOwnProperty("key")) { ... }`
- nullの可能性があるときには`obj?.a?.b`
    - `widget?.window?.title ?? "未定義"`
    - `[]`でも使える
        - `languages?.[ja]?.[messageKey]`
- `toString()`と`String()`は同じ
- オブジェクトのプロパティ名は暗黙的に文字列に変換される
- オブジェクトを配列にする
    - `Object.keys(obj)`でkeyの配列にする
    - `Object.values(obj)`でvalueの配列にする
    - `Object.entries(obj)`でkeyとvalueの配列にする
- `Object.assign`で複製やマージができる
    - `const merged = Object.assign({}, objectA, objectB);`
        - プロパティが重複すると後ろで上書きされる
    - `const merged = { ...objectA, ...objectB };`でもマージできる
    - `const cloneObj = Object.assign({}, obj);`で浅い複製できる

### プロトタイプオブジェクト

- 空オブジェクトでも`.toString()`を呼び出せる
    - `Object.prototype`のメソッドを継承しているから
        - プロトタイプメソッド
            - 同名のインスタンスメソッドがあればそれが優先
- `hasOwnProperty`と`in`の違い
    - `in`はプロトタイプまで遡る
- `Object.create(null)`で本当に空のオブジェクトを作れる
    - 昔は`Map`の代わりに使われていた
        - `const obj = {};`でも`obj["toString"]`でアクセスできてしまうから


### 配列

- 最後の要素へのアクセスは`array[array.length - 1]`でできる
- 存在しないインデックスへのアクセスは`undefined`になる
    - オブジェクトでも同じ
- 配列は常に`length`の数だけ要素を持っているとは限らない
    - 疎な配列
        - `const sparseArray = [1,, 3];`
            - `sparseArray.length`は`3`となる
                - `sparseArray[1]`は`undefined`
- 配列かどうか確認するには`isArray`
    - `typeof array`では`"object"`となる
- 配列も分割代入できる
- 疎な配列があるから本当に未定義なものと区別できない
    - `hasOwnProperty`で区別する
        - `sparseArray.hasOwnProperty(1)`が`false`となる
- どの位置にあるか知りたい
    - `indexOf`と`lastIndexOf`
        - `ary.indexOf("JS");`
            - なければ`-1`が返ってくる
    - オブジェクトには`findIndex`を使う
        - `colors.findIndex((obj) => { return obj.color === "blue" });`
- 条件に一致する要素を取得する
    - `colors.find((obj) => { return obj.color === "white" });`
- 指定範囲の要素を取得する
    - `slice(1, 4)`
- 目当てのものが含まれているか確認する
    - `inclues`
        - オブジェクトには使えない
            - `some`
                - `colors.some((obj) => return obj.color === "blue"; });`
- 追加と削除
    - `push`と`pop`
    - `unshift`と`shift`
- 結合したい
    - `array.concat(["D", "E"]);`
- flattenにしたい
    - `newArray = ["X", "Y", "Z", ...array];`
        - `["X", ...array, "Z"]`もできる
    - `newArray = ["X", "Y", "Z"].concat(array);`
    - ES2019なら`flat(Infinity)`も使える
        - これ以上フラット化できなくてもそのまま返す
- 任意のインデックス要素を削除する
    - `array.splice(インデックス, 削除する要素数)`
        - 自動的に詰められるから疎にはならない
- すべての要素を削除
    - `array.length = 0;`とすると配列が空になる
        - その要素数に切り詰められるから
- 注意する破壊的メソッド
    - `sort`
- 非破壊でコピーする
    - `array.slice()`
    - `array.concat()`
        - 引数なしで呼び出すとコピーを返す
            - コピーしてから破壊的メソッドを使う
- 指定の要素だけ集める
    - `array.filter((currentValue, index, array) => { return currentValue % 2 == 1; });`
- `reduce`
    - `array.reduce((累積値, 要素, インデックス, 配列) => { return 処理;}, 初期値)`


### 文字列

- 分解したり繋げたりする
    - `const strings = "赤・青・黄".split("・").join("、");`
- 正規表現を使って抜き出す
    - `const strings = str.split(/\s+/);`
- "?"以降を抜き出す
    - `const indexOfQuery = url.indexOf("?"); const queryString = url.slice(indexOfQuery):`
        - `slice`と`substring`はほとんど同じ
- 文字列の検索
    - `str.startsWith()`
    - `str.endsWith()`
    - `str.includes()`
- 正規表現
    - `const patternA = /パターン/フラグ;`
    - `const patternB = new RedExp("パターン文字列", "フラグ");`
        - 関数として呼び出されるまで評価されない
            - 動的に変更できる
    - `str.search()`
    - `"文字列".match(/パターン/);`
        - マッチしない時はnullを返す
        - `/[a-zA-Z]+/g`で見つかっても最後までやる
        - `/バターン1(パターン2)/`でカッコを取り出せる
    - そのパターンにマッチするものがあるか調べる
        - `/^にわ/.test(str)`
            - 繰り返しや文字の集合なども検索できる
    - 基本はStringメソッドでやる
        - 柔軟性や曖昧検索のときは正規表現+コメントする
- 置換
    - `str.replace("文字", "");`
    - `str.replace(/文字/, "");`
        - コールバックもできる
            - `dateString.replace(/(\d{4})-(\d{2})-(\d{2})/, (all, year, month, day) => { ... };`
- URLは`getResource()`
    - `scheme`、`host`、`pathname`
        - 最後の`/`は削除してから使う
            - Node.jsのPathモジュールを使う
- タグ付きテンプレート関数
    - tag`template ${0} literal ${1}`;
    - `function tag(strings, ...values) { ... };`
        - valuesが取れる
    - `String.raw()`

### 文字列とUnicode

- JSでは「文字列は`Code Unit`が順番に並んだもの」として扱われる
    - リンゴの絵文字の length は 2 になる
        - サロゲートペア
    - `const codePoints = Array.from("リンゴ🍎"); // => ["リ", "ン", "ゴ", "🍎"]`
        - 完璧ではない
            - ビルトインだけでは難しい
- 正規表現のときは`u`をつける
    - `const [all, fish] = "𩸽のひらき".match(/(.)のひらき/);`
        - 文字化けする
    - `const [all, fish] = "𩸽のひらき".match(/(.)のひらき/u);`
        - 基本的には`u`を付ける



### ラッパーオブジェクト

- なぜ型がメソッド呼び出しできるのか
    - プリミティブ型は自動的にラッパーオブジェクトに変換されるからメソッド呼び出しできる
        - JSはすべてがオブジェクトのように「見える」
            - すべてがオブジェクトではない


### 関数とスコープ

- 関数を定義する
    - 新しいスコープを作るということ
- スコープとは
    - 参照できる範囲を決めるもの
- 関数スコープ
    - `function fn() { 関数スコープ };`
- ブロックスコープ
    - `if() { ブロックスコープ };`
- スコープチェーン
    - `{ { } }`
        - 内側から外側を順番に参照できる
- グローバルスコープ
    - グローバル変数
    - ビルトインオブジェクト
        - `undefined`や`isNaN`
        - `Array`や`RegExp`
        - `document`や`module`
- むやみにグローバルスコープへ変数定義しない
    - 外側の変数が隠蔽されるから
        - shadowing
        - 関数を使って小さなスコープにして書く
- `var`は巻き上げする
    - ブロックスコープを無視してしまう
- クロージャー
    - 「外側のスコープにある変数への参照を保持できる」
        - グローバル変数を減らせる
        - 高階関数を作る
    - 静的スコープ
        - 変数の中身は静的に決まる
            - 内側にいないなら一つ外側のスコープを確認する
    - メモリ管理の仕組み
        - 解放はあくまでそのデータが参照されているかどうかで決まる


### 関数とthis

- 実行コンテキスト
    - Script
        - `this`は`window`オブジェクト
    - Module
        - `this`は`undefined`
            - ES2020では`globalThis`
- アロー関数以外の関数のthis
    - ベースオブジェクト
        - `self`のようなもの
            - 実行時に決定される
        - なければ`undefined`
    - `call`、`apply`、`bind`で明示的に指定できる
- アロー関数の`this`
    - 外側で最も近い関数の`this`となる
        - 静的に決まる

### 非同期処理:コールバック/Promise/Async Function

- 同期的なブロック処理
    - ブラウザでは大問題になる
        - スクロールが効かなくなる
- 非同期処理はメインスレッドで実行される
    - 非同期処理も同期処理の影響を受ける
    - **非同期なタイミングで実行される処理**
- 普通に`try...catch`書くと非同期処理のエラーは処理できない
- エラーファーストコールバック
    - 共通ルールの一つ
        - 処理が失敗したら`error`にエラーオブジェクトを渡す
        - 処理が成功なら2番目以降の引数に結果を渡す
            - `fs.readfile("./example.txt", (error, data) => { if(error) { ...} else { ... } });`
- Promise
    - 成功したら`resolve`
    - 失敗したら`reject`
        - `const executor = (resolve, reject) => {};`
    - `then`メソッドで成功時と失敗時の処理を渡す
    - `try...catch`を使わなくても例外がキャッチされる
        - `catch`は`promise.then(undefined, onRejected)`のシンタックスシュガー
- Promiseの状態
    - Fullfilled
        - `resolve`成功したとき
    - Rejected
        - `reject`失敗したとき
    - Pending
        - FullfilledまたはRejectedではないとき
        - `new Promise`でインスタンスを作成したとき
            - 最初はPendingで、一度でも変化したらそこからは変わらない
                - `resolve`した後の`reject`は呼び出されない
                - `resolve`した後にもう一度`resolve`しても呼び出されない
- `Promise.resolve`
    - 最初からFullfilledな`Promise`インスタンスを作る
        - `Promise.reject`もある
            - テストコードで使われる
                - 短く書けるから
- Promiseチェーン
    - 失敗時は一番近い失敗処理が呼び出される
        - 途中の`then`は無視される
            - 失敗を一度キャッチするとまたチェーンに戻る
                - `catch`はFullfilled状態のPromiseインスタンスを作成するから
                - `return Promise.reject(new Error("失敗"));`
                    - これはRejected状態となる
                        - キャッチしてもRejected状態を継続できる
        - 例外時も同じ
    - `return`で値を返すと次の`then`へ引数として渡せる
    - `Promise#finally`は必ず呼び出される
        - `isLoading`を`false`にするなど
    - `then`ごとに配列に値を`push`していく使い方もできる
        - `Promise.all`で複数のPromiseもまとめられる
            - どれを先に取得しても問題ないとき
                - 一つでもRejectedとなったら失敗処理が呼び出される
    - `Promise.race`
        - 1つでもSettled状態になれば次の処理をする
            - 非同期処理のタイムアウトが作れる
                - 一定時間経過しても処理が終わってないならエラーとする
                    - `timeout`とやりたい処理を`Promise.race`の引数にする
- Async Function
    - `async function doAsync() { return "値"; }`
    - `function doAsync() { return Promise.resolve("値"); }`
        - 同じ
            - 必ず`Promise`インスタンスを返す
                - `Promise`を返すただの関数と何も変わらない
            - `await`が使える
    - どれにでもつけられる
        - `async function fn1() {}`
        - `const fn2 = async function() {};`
        - `const fn3 = async() => {};`
        - `const obj = { async method() {} };`
- `await`
    - 同期処理のように書ける
    - `Promise`のresolveされた値が`await`の返り値になる
        - `const value = await Promise.resolve(42);`
            - 例外があったらRejectedなPromiseが返る
                - `try...catch`構文でキャッチできる
                    - 同期処理と同じ
    - 非同期でもループ処理ができる
    - `Promise.all`も使える
        - Promiseを複数作ってから`Promise.all`に渡す
            - `const promises = resources.map(function(resource) { return fetch(resource); });`
                - `const responses = await Promise.all(promises);`
    - Async Funtion の中でのみ使える
        - 外の処理は止まらない
            - UIなどの処理が止まってしまうから
            - コールバック関数の時に注意
                - `for`を`forEach`に単純に変えられない
                - `Promise.all`でまとめるか

### Map/Set

- Map
    - 初期値で渡せるのはエントリーの配列
        - `new Map([["key1", "value1"], ["key2", "value2"]]);`
    - `size`で数がわかる
    - `set`で追加する
        - 同じキーは上書きされる
    - `get`で取り出す
    - `has`でそのキーがあるか確認できる
    - `delete`で削除する
    - `clear`で全て削除する
    - `keys`,`values`が返すのは配列ではない
        - `Array.from(map.keys());`
            - 配列に変換して反復処理をする
    - マップとしての`Object`との違い
        - デメリット
            - `Object`はprototypeメソッドで意図しない動きをすることがある
                - 昔は`Object.create(null)`のようにして使っていた
                    - `Map`が導入された
            - キーは`Symbol`のみ
        - メリット
            - リテラル表現で作成しやすい
            - `JSON.stringify`で変換できる
            - 多くの場所で使われている
- WeakMap
    - キーを弱い参照で持つ
        - ガベージコレクションを妨げない
    - iterableではない
        - `keys`や`size`がない
    - 使い方
        - イベントリスナーを管理する
            - 使われなくなったら消える
        - キャッシュとして一時的に計算結果を保存する
- Set
    - 同じ値を入れると1つのみ格納される
    - インデックスはない
    - `forEach`が使える
- WeakSet
    - データの一意性を確認することに特化したセット


### JSON

- `JSON.parse`
    - `json = [1, 2, 3]`なら返り値も配列
    - パースできないと例外が投げられる
        - 基本的に`try...catch`される
- `JSON.stringify`
    - 第二引数に`replacer`を渡せる
        - keyがnullならundefinedにする処理
        - ホワイトリストとしてkeyの配列を渡せる
            - `["id", "name"]`だけを抜き出す
    - 第三引数にフォーマット時のインデントを設定できる
        - `JSON.stringify(obj, null, 2)`ならスペース2個でインデント
            - `"\t"`もできる
    - Symbolやundefinedは変換されない
    - オブジェクト内に`toJSON`がある場合
        - その返り値のみ使う
            - 特殊な形式でシリアライズできる


### Date

- `Date.UTC(2006, 0, 2, 15, 4, 5, 999)`
    - UTCなのでローカルのタイムゾーンに影響されない
- `getMonth`で取得すると`+1`する必要がある
    - `const mm = String(date.getMonth() + 1).padStart(2, "0");`
- ほとんどライブラリを使う
    - `moment()`

### Math

- 乱数をつくる
    - `Math.random()`


### ECMAScriptモジュール

- なぜモジュールを使うのか
    - 保守性
        - 依存性の高いコードをまとめて、他への依存性を減らせる
    - 名前空間
        - グローバルを汚染しない
    - 再利用性
        - コピペせずに再利用できる
- 名前つきエクスポート／インポート
    - `export { foo };`
    - `export function bar() {};`
    - `import { foo, bar } from "./my-module.js";`
    - エイリアス
        - `export { internalFoo as foo };`
        - `import { foo as myFoo } from "./named-export-alias.js";`
- デフォルトエクスポート／インポート
    - デフォルトエクスポートに名前をつけてインポートする


### ECMAScript

- その機能がどのような経緯で入ったのかを調べる手段を持っておく
    - その機能が何を解決するために導入されたのかを知る
        - 調べたいと思ったときに調べることができるように、調べ方を知っておくことが重要