🚀

【技術書まとめ】改訂3版JavaScript本格入門 ~モダンスタイルによる基礎から現場での応用まで

2023/12/21に公開

本書を読んだ目的

現在のJSを体系的に捉えるために本書を手に取った。
印象に残った記述をまとめておく。

1: イントロダクション

  • フレームワークとはPCでいうマザーボードのようなもの
    • (たとえがわかりやすい)

2: 基本的な書き方

  • <scipt>要素は何度記述されても最終的にひとまとめで解釈される
  • 基本型(stringなど)と参照型(object, arrayなど)でデータの扱いが違う
  • falsyな値
    • 空文字列(""), ゼロ値(0, -0, 0n), null/undefined, NaN
      • これ以外はtruthyな値
  • 数値リテラル
    • 整数リテラル
      • 0b(2進数)
      • 0o(8進数)
      • 0x(16進数)
    • 浮動小数点リテラル
      • 0.1234e4(= 0.1234*10の4乗)
      • 数値セパレーターで可読性を高める(1_234_567)

3: 演算子

  • 加算演算子(+)以外の演算子はなんとかして数値として処理しようとする
  • 小数点を含む演算に注意する
    • 0.2 * 3 // 0.6000000000000001となる
      • 内部的に2進数で演算しているから
    • decimal.jsを使用してもいい
  • 変数とはメモリ上の場所に対して付けられた名札ともいえる
  • 定数は再代入できないだけ
    • 配列の中身の書き換えは可能
  • 分割代入
    • const {a, b, c, ...other} = data;
    • 必要なものだけ抜き出す
  • ==は緩い等価演算子
    • 左分/右辺の型がをなんとか等しいと見なせないかと試みる
  • typeofで意味のある型を返すのはプリミティブ型だけ
    • 配列などの参照系はobjectとなる

4: 制御構文

  • ifやforなど

5: 組み込みオブジェクト

  • lengthはサロゲートペアに注意する
    • [...str].lengthのように文字配列に分解するときちんと認識する
  • normalizeでUnicode正規化する
  • NaNは自分自身を含むすべての値と等しくない
    • Number.NaN === Number.NaN) // false
  • 配列から任意の要素を取り出す
    • list[Math.floor(Math.random() * list.length)];
  • 配列操作図解
  • 完全に平坦化するにはlist.flat(Infinity)
  • Array.fromで配列をコピーするとシャローコピーになる
    • 入れ子になっている場合、元配列への修正がコピー先に影響する
      • オブジェクトへの参照だけが複製されるから
  • Mapのキーは===で比較される

6: 関数

  • function命令はスコープの先頭で定義されたとみなされる
    • 巻き上げ(hoist)
    • 逆に、関数リテラル、アロー関数、Functionコンストラクターは実行時に評価される
      • 呼び出しもとコードよりも先に記述する
  • スコープの異なる変数は、名前が同一であっても異なる変数と見なされる

7: Objectオブジェクト

  • プロトタイプなど

8: オブジェクト指向構文

  • クラスの基本など

9: DOM

  • DOMとはHTML文書をオブジェクトとして使う仕組み
    • マークアップ言語で書かれたドキュメントにアクセスする標準的な仕組み
  • 要素ノードを取得する
    • idで: getElementById('result')
    • セレクター式で: querySelector('#list .external')
    • セレクター式で複数: querySelectorAll('#list .external')
  • 子要素を取得する
    • 取得メソッドで特定の要素を取得したあと、近接ノードはノードウォーキングで取得する
      • 例: selectタグを取得してから子要素のoptionタグを取得する
    • childNodesプロパティで取得する
  • イベント駆動型モデル
    • イベントの発生を検知して実行する: addEventListener('mouseenter', ...)
    • <script>要素の記述位置によって、対象の読み込み前に使用してエラーが起こることがある
      • 対応方法
        • DOMContentLoadedイベントリスナーでくくる
        • defer属性をつける
          • ページロード/解析を終えたタイミングでコードを実行する
  • 属性値を取得する
    • getAttribute('title')
    • すべて取得する: img.attributes
      • NamedNodeMapとして返す
        • ノードの順序は保証されない
  • 属性値を設定する
    • setAttribute('src', 'images/noimage.jpg')
    • hasAttribute('src')で属性の存在を確認できる
  • 要素のプロパティを取得/設定する
    • querySelectorで取得したmember.valueは現在の値だが、member.getAttribute('value')で取得した値は初期値となる
  • テキストを取得/設定する
    • .textContentを使う
    • 入力値をそのままinnerHTMLで使うと、クロスサイトスクリプティング(XSS)脆弱性となる
  • フォーム要素にアクセスする
    • selectタグ.value等で取得できる
      • 複数選択可であればselectedになっているものの値を取得する
    • ラジオボタン/チェックボックスでは、inputタグがcheckedになっているものの値を取得する
  • アップロードされたファイルの情報を取得する
    • おもなプロパティはname, type, size, lastModified
    • FileReaderを使用して読み込める
      • バイナリを読み込む: readAsDataURL()
  • ノードを追加/置き換え/削除する
    • 追加など
      • 作成: createElement()
      • 追加: append()
      • 属性の作成: createAttribute()
      • 属性の追加: setAttributeNode()
      • テキストノードの作成: createTextNode()
      • 要素を挿入先の子要素の先頭に追加する: prepend()
      • 要素を挿入先の前に追加する: before()
      • 要素の挿入先の末尾に追加する: after()
    • ulにliをappendしていくと、その度に再描画されてしまう
      • DocumentFragmentオブジェクト上で組み立ててからappendする
        • document.createDocumentFragment()
    • data-xxxx属性でイベントリスナーで使用するパラメーターを埋め込める
    • HTMLCollection/NodeListは文書ツリーを参照しているため、リアルタイムに変更が反映される
  • イベントオブジェクトの基本
    • イベントオブジェクトのメンバー
      • 一般: target, currentTarget, type(click等)など
      • 座標: clientX, screenX, pageX, offsetXなど
      • キーボード/マウス: button, key, shiftKeyなど
    • イベント処理をキャンセルする
      • バブリングをキャンセルする: e.stopPropagation()
      • バブリングを直ちにキャンセルする: e.stopImmediatePropagation()
        • 同じ要素のイベントリスナーも実行しない
      • イベント本来の挙動をキャンセルする: e.preventDefault()
        • cancelableなイベントだけキャンセル可能
    • data-xxxx属性はelement.dataset.propでも操作できる
      • 例: <input ... data-add="1"/>input.dataset.add = 2でセットする
    • currentTargetはイベントリスナーが登録された要素を常に取得する
      • targetはイベントの発生元を取得する
    • イベントの動作オプションを指定する
      • 初回イベントだけを処理する: { once: true }
    • asyncとdeferの違い
      • asyncはページ解析を待たずにダウンロードでき次第、実行する
    • integrity属性はコードの改ざんを防ぐ

10: クライアントサイドJavaScript開発を極める

  • ブラウザーオブジェクトの階層構造
  • タイマー機能 setInterval, setTimeout
    • あくまで指定された時間にキューに処理を登録するだけ
      • シングルスレッド処理だから
      • 正確な時間とは限らない
  • ウィンドウサイズ
  • スクロール位置を設定/取得する
    • 指定位置までスクロール(絶対座標): scrollTo(x, y)
  • 表示ページのアドレス情報を取得/操作する locationオブジェクト
    • locationオブジェクト
      • hash: #gihyo
      • host: wings.msn.to:8080
      • hostname: wings.msn.to
      • pathname: /js/sample.html
      • search: ?id=12345
    • hrefプロパティで移動すると履歴が残る(戻るボタンで戻れる)
      • 履歴を残したくない場合はreplaceメソッドを使う
      • assignメソッドは、実行元コードと操作先のオリジン(プロトコル://ドメイン:ポート)が違う場合にアクセスをガードする
  • 履歴に沿ってページを前後に移動する historyオブジェクト
    - history.back(), history.forward()
  • JSによる操作をブラウザーの履歴に残す
    • pushStateメソッド
      • History APIとも呼ばれる
  • 便利なログメソッド
    • スクリプトの実行時間を計測する: console.time()
    • オブジェクトを見やすい形式で出力する: console.dir()
  • ユーザーデータを保存する Storageオブジェクト
    • ローカルストレージ: オリジン単位でデータを管理する。
      • ウィンドウ/タブを跨いでデータの共有が可能
        • ほかのホストの保存データを読み出すことはできない
      • 明示的にデータを削除しないとデータが消えない
      • 同一オリジンで複数アプリを稼働していると変数名が衝突しやすい
    • セッションストレージ: 現在のセッション(ブラウザーが開いている間)でだけ維持されるデータを管理する
      • ウィンドウ/タブ間でデータを共有することはできない
    • オブジェクトをストレージに保存する場合は復元可能なJSON化する
      • 保存: storage.setItem('apple', JSON.stringify(apple));
      • 読み取り: JSON.parse(storage.getItem('apple'));
    • storageイベントリスナー
      • key: 変更されたキー
      • oldValue: 変更前の値
      • newValue: 変更後の値
      • url: 変更発生元のページ
      • storageArea: 影響受けたストレージ(localStorage/sessionStorage)
  • Fetch API
    • Responseオブジェクト
      • ステータス
        • ok: 成功したか
        • status: HTTPステータスコード
      • ヘッダー
        • headers: Headersオブジェクト
        • type: レスポンスタイプ
      • 本文
        • body: レスポンス本体を取得(ReadableStreamオブジェクト)
        • blob(): Blobとして取得
        • json(): JSONとして取得
    • fetchは404などの問題をエラーとはみなさない
      • res.okで200番台のコードが返されているかを確認する必要がある
    • クエリ情報はURLSearchParamsオブジェクトで組み立てる
      • params.toString()で使用する
        • 自動的にエスケープ処理される
    • POSTデータはFormDataオブジェクトで組み立てる
      • append: 名前name、値valueでフィールドを追加する
      • entries: すべてのキー/値を取得する
      • getAll(name): 指定の名前のフィールドをすべて取得する
  • 異なるオリジンにアクセスする
    • CORS
      • サーバー側のコードからオリジンをまたぐのはエラーとならない
  • Promiseオブジェクト
    • Promiseの状態管理
  • Web Worker
    • JSによる処理をマルチスレッド化するための仕組み
      • ワーカーはバックグラウンドで動作するJSコードのこと
        • postMessage()でパラメーターを送信する
        • messageイベントリスナーで結果を処理する
    • 高度の数学計算などCPUを占有するような処理はWeb Workerを利用する
    • ワーカーではDOMツリーを操作できない

11: 現場で避けて通れない応用知識

  • Vite, ESlint, JSDocなど

Discussion