Open43

JavaScrip Primer

koukou

thisはいろんな本を読んでやっと理解できるようになってきた

コンテキストの概念をちゃんと理解することで頭の中でイメージがしやすくなった気がする。

thisの参照先は主に次の条件によって変化する。

  • 実行コンテキストにおけるthis
  • コンストラクタにおけるthis
  • 関数とメソッドにおけるthis
  • Arrow Functionにおけるthis
koukou

プロトタイプオブジェクトの概念はPythonでもあるから動的型付け言語によく用いられる手法なのかな?

koukou

メソッド呼び出しにおけるthisが問題となるパターン1

完全に理解した気がする
問題: thisを含むメソッドを変数に代入した場合

"use strict";
const person = {
    fullName: "Brendan Eich",
    sayName: function() {
        // `this`は呼び出し元によって異なる
        return this.fullName;
    }
};
// `sayName`メソッドは`person`オブジェクトに所属する
// `this`は`person`オブジェクトとなる
console.log(person.sayName()); // => "Brendan Eich"
// `person.sayName`を`say`変数に代入する
const say = person.sayName;
// 代入したメソッドを関数として呼ぶ
// この`say`関数はどのオブジェクトにも所属していない
// `this`はundefinedとなるため例外を投げる
say(); // => TypeError: Cannot read property 'fullName' of undefined

対応方法

  • メソッドを変数に代入するな。
  • thisを明示的に指定する
  • 関数をラップする
koukou

メソッド呼び出しにおけるthisが問題となるパターン2

問題: コールバック関数とthis

"use strict";
// strict modeを明示しているのは、thisがグローバルオブジェクトに暗黙的に変換されるのを防止するため
const Prefixer = {
    prefix: "pre",
    /**
     * `strings`配列の各要素にprefixをつける
     */
    prefixArray(strings) {
        return strings.map(function(str) {
            // コールバック関数における`this`は`undefined`となる(strict mode)
            // そのため`this.prefix`は`undefined.prefix`となり例外が発生する
            return this.prefix + "-" + str;
        });
    }
};
// `prefixArray`メソッドにおける`this`は`Prefixer`
Prefixer.prefixArray(["a", "b", "c"]); // => TypeError: Cannot read property 'prefix' of undefined

対応方法

  • thisを一時変数へ代入する(下を使うことが多い)
  • Arrow Functionでコールバック関数を扱う
koukou

Arrow Functionとthis

  • Arrow Function で定義されたthisは関数の定義時(静的)に決まる
  • Arrow Function以外は関数の実行時(動的)に決まる

静的に決まるとは、Arrow Functionの外側のスコープ(関数)のthisを参照する。
さらに、Arrow Function はthisを暗黙的な引数として受け付けない

koukou

変数におけるスコープチェーンの仕組みと同じだな〜

コンテキストの種類とスコープ範囲分かると繋がってくるな

koukou

急にメソッド短縮記法を書かないで

いや、出てたけど忘れてただけか

koukou

this理解するの大変すぎるけど何度目かの挑戦でもう少しで理解できそうだからちゃんと理解するか

koukou

はえー、知らなかった。

Errorオブジェクトはインスタンスの作成時に、そのインスタンスが作成されたファイル名や行数などのデバッグに役立つ情報を持っています。

console.error()で出力するとスタックトレースもコンソールに出力してくれるらしい。

koukou

ビルトインエラー

  • ReferenceError
  • SyntaxError
  • TypeErro
koukou

元のエラーオブジェクトのスタックトレースが失われる

エラーオブジェクト受け取って再度エラーオブジェクトを返すと、元のエラーオブジェクトのスタックトレースが失われる。

それを解決するにはErrorオブジェクトのcauseオプションを使用する

} catch (e) {
    throw new Error("二つ目のエラー", {cause: e})
}
koukou

実行順番について

なんかエラーをキャッチできないやつあったけどそういうことだったのかー!と理解した。tryという実行コンテキストがあってその中のエラーだけをキャッチしているイメージなのかな?

非同期処理では、try...catch構文を使っても非同期的に発生した例外をキャッチできません。 次のコードでは、10ミリ秒後に非同期的なエラーを発生させています。 しかし、try...catch構文では次のような非同期エラーをキャッチできません。

try {
    setTimeout(() => {
        throw new Error("非同期的なエラー");
    }, 10);
} catch (error) {
    // 非同期エラーはキャッチできないため、この行は実行されません
}
console.log("この行は実行されます");
koukou

Promiseオブジェクトは非同期処理の状態や結果をラップするオブジェクトという説明がしっくりきた。

koukou

catchに.thenを繋ぐとエラー後に実行される

以下の仕様があるため、catchに.thenを繋ぐとエラー後に実行される。
catchはFulfilled状態のPromiseインスタンスを作成して返すため
thenやcatchメソッドは常に新しいPromiseインスタンスを作成して返すという仕様

koukou

[ES2022] Module直下ではawait式が使える

確かに、modulesではawaitそのまま使いたい時あるかも

koukou

[コラム] エラーファーストコールバック

元のエラーオブジェクトのスタックトレースが失われる話のやつか

koukou

Map/Set

簡単かと思ったらこれも重たかった。
Objectと同じかと思ったら結構違くてびっくり
オブジェクトをキーにとれるからDOMノードや要素に付加情報を付けれるのとかなるほどなと思ったけど、明日になったら大体忘れてそう。
使う場面は多いのかな?実務で出てきたらどう使われているか見るのが一番理解しやすそう

koukou

イテレータとジェネレータ

概念はあったけど名前は知らなかった

koukou

メモリ効率は配列をループするより、mapの方が効率良いってのは知ってたけどキミのおかげか?

koukou

必要なタイミングで値を評価できる仕組みを遅延評価(lazy evaluation)と呼びます。

koukou

Iterableなビルトインオブジェクト

配列、文字列、Map、Set、ジェネレータ関数

koukou

イテレータジェネレータ単元終わり!

koukou

JSON

これもなんとなく使ってるやつ!

koukou

JSON.stringify静的メソッドにはオプショナルな引数が2つあります。

第二引数はreplacer引数
関数か配列を渡せて、関数を渡せば文字列に変換される挙動をコントロールできる。配列では許可リストとして使われ、その配列に含まれるプロパティのみ変換される。

これは使えそう!!使いたい場面が想像できた。

// 関数渡すよ〜
const obj = { id: 1, name: "js-primer", bio: null };
const replacer = (key, value) => {
    if (value === null) {
        return undefined;
    }
    return value;
};
console.log(JSON.stringify(obj, replacer)); // => '{"id":1,"name":"js-primer"}'
// 配列渡すよ〜
const obj = { id: 1, name: "js-primer", bio: null };
const replacer = ["id", "name"];
console.log(JSON.stringify(obj, replacer)); // => '{"id":1,"name":"js-primer"}'
koukou

第三引数はspace引数
インデントの設定
これはプロジェクトに従うので使わなそう!!

const obj = { id: 1, name: "js-primer" };
// replacer引数を使わない場合はnullを渡して省略するのが一般的です
console.log(JSON.stringify(obj, null, 2)); // 2を"\t"にするとタブでインデント
/*
{
   "id": 1,
   "name": "js-primer"
}
*/
koukou

配列ないに変換できない値があるとnullにしてくれる

JSON.stringify静的メソッドはJSONで表現可能な値だけをシリアライズします。 そのため、値が関数やSymbol、あるいはundefinedであるプロパティなどは変換されません。
ただし、配列の値としてそれらが見つかったときには例外的にnullに置き換えられます。

koukou

MapやSetのインスタンスは空のオブジェクトに!

// 値がMapのプロパティ
const map = new Map();
map.set("foo", "foo");
console.log(JSON.stringify({ x: map })); // => '{"x":{}}'
koukou

toJSONメソッドを使ったシリアライズ

これはなんであるん?

オブジェクトがtoJSONメソッドを持っている場合、JSON.stringify静的メソッドは既定の文字列変換ではなくtoJSONメソッドの返り値を使います。

const obj = {
    foo: "foo",
    toJSON() {
        return "bar";
    }
};
console.log(JSON.stringify(obj)); // => '"bar"'
console.log(JSON.stringify({ x: obj })); // => '{"x":"bar"}'
koukou

Date

Dateにも学びはあるのか
Day.js使うけど中身はちゃんと理解しといた方が良いか?

koukou

第二部:応用編(ユースケース)
Ajax通信

koukou

関数を分けた時にどこにどこまでの責務を持たせるか悩む
特にエラーの管理範囲がわからない

koukou

その辺のエラーハンドリングに関する記事とかないかな
その時によって変わるから難しいのか

koukou

とりあえず、APIを使ってgithubの情報を取得するのはできた
リファクタリングの方針をもっと抽象的に理解していきたい

koukou

TodoApp

const app = new App();がReactでの読み込みに似ててなんだか伏線回収しているみたい。

koukou

JavaScript PrimerのTodoアプリ舐めてました。
オブジェクト指向を学べる良い教材だな

koukou

これは必修科目ではないか?
Reactやる前にやるべきとは言い難いが、Reactをやった後にやるべき教材だと思う。
Todoアプリを構成する要素をModel, Viewという単位でモジュールに分けて実装するところなど本当に参考になる。JavaScriptでクラスを使うことはほとんどないと思うが、こうした考え方を知っておくことはプログラムの土台を固める上で非常に大事な要素となることは間違いなさそう。

koukou

前職にいたつよつよフロントエンジニアがやっていた構成ってちゃんとしたモデルビューの構成になっていたの思い出して感動している。
ロジックはカスタムフックにカプセル化されていたし、アイテムのビューとリストのビューでもちゃんと分けられていたしテストもしやすい構成になっていたんだろうな。
ちゃんとした構成のアプリ作りたくなってきた。ESlintもしっかりやって、Playwright MCPを使ってテストの自動化までしたい。テストの自動化ってどの企業でも課題にしてそうなんだよな。