すもぎのめも

いろいろあったことをメモしています

「開眼! JavaScript ―言語仕様から学ぶJavaScriptの本質」を読んだ

なぜ読んだか

結構前に購入したものの読めていなかった。TypeScript を深く知るために、先に JavaScript を復習しておくことにした。また 世界一流エンジニアの思考法 (文春e-book) を読んで、脳の負荷を下げるために基礎を再構築・積本を片付けて「完了」を得たい、というのもある。

どんな本か

初心者向きではなく、詳細すぎでもない、JavaScript の言語仕様を俯瞰できる本で、ページ数も少ないためさらっと読める。 付録Aがおさらいに便利で、ライブラリのコードを読み書きするなら必修になるリスト。ここから更に深く潜るのがよさそう。

1章から3章は JavaScript オブジェクトについて

  • 1.18 すべてのコンストラクタのインスタンスはコンストラクタ関数にポイントする constructor プロパティを持つ

何がこのインスタンスを生成したかを確かめる時に使用できる。

// オブジェクトを生成すると constructor プロパティが自動生成される
const foo = {}
console.log(foo.constructor === Object)
console.log(foo.constructor)

// これはプリミティブ型でも同じ
const n = 39
console.log(n.constructor)

// ユーザー定義の場合は環境によって出力が異なる場合がある
const User = function User() {
    return 'xxx'
}
const smg = new User()

console.log(smg.constructor === User)
console.log(smg.constructor)

4章は関数について

  • 4.8 arguments はすべての関数本体で利用できる 関数に定義されていない引数を渡してもエラーにならないことは理解していたが、それを arguments オブジェクトで使用できることは知らなかった。
const add = function() {
    // 関数の中から参照できる
    return arguments[0] + arguments[1]
}
console.log(add(2, 4))  // 6
  • 4.11 引数を再定義 引数に値を再代入しても arguments に再代入しても、どちらも連動して再代入された値に変更される。 arguments はただの配列ではないということ。
const foo = function(x) {
    arguments[0] = 123
    console.log(x, arguments[0])  // 123 123

    x = 456
    console.log(x, arguments[0])  // 456 456
}
  • 4.17 無名関数を即時実行 ! を使うとコードを短くして即時実行できる。Boolean に評価されて、そのタイミングで実行されているんだろうか。謎。
// これは実行される
!function run(msg) {console.log(msg)}('aaaaa')
// これは実行されない
function notRun(msg) {console.log(msg)}('bbbbb')

7章と8章は関数について

  • 7.7 クロージャはスコープチェーンによって生成される

    クロージャを一言で表すと「スコープチェーンに存在する変数への参照を保持している関数」

クロージャは他の言語と表向きは同様。

  • 8.8 prototype プロパティに新しいオブジェクトを設定するとデフォルトの constructor プロパティを失う オブジェクト指向 JavaScript では prototype を書き換えることがあるらしいが、変更を元に戻す場合は元の prototype を保持しておかなければならない様子。戻すことが必要になるケースはほとんどないと思うけど。
const Foo = function Foo() { }

Foo.prototype.bar = 123

// 後で比較する用
const oldProtoType = Foo.prototype

const oldFoo = new Foo();
console.log('変更前の constructor は Foo か', oldFoo.constructor === Foo);
console.log('変更前の constructor は Function か', oldFoo.constructor);
console.log('変更前は bar にアクセスできるか', oldFoo.bar)

// prototype に新しいオブジェクトを設定すると...
Foo.prototype = {}

const newFoo = new Foo()
console.log('変更後の constructor は Foo か', newFoo.constructor === Foo);
console.log('変更後の constructor は Function か', newFoo.constructor);
console.log('変更後は bar にアクセスできるか', newFoo.bar)

// 復活させるには constructor プロパティを持ったオブジェクトを設定
Foo.prototype = { constructor: Foo }

const rebornFoo = new Foo()
console.log('復活後の constructor は Foo か', rebornFoo.constructor === Foo);
console.log('復活後の constructor は Function か', rebornFoo.constructor);
console.log('復活後は bar にアクセスできるか', rebornFoo.bar)

console.log(oldProtoType === Foo.prototype)
console.log('変更前の prototype には bar があるが', 'bar' in oldProtoType)
console.log('変更後には当然ない bar' in Foo.prototype)

9章 配列

  • 9.9 配列の length を設定して要素の追加・削除 length は readonly ではなく再代入できる。現在値より大きい場合に要素は undefined で埋められ、小さい場合に超過した要素が削除される。
const nplb = ['nene', 'polka', 'lamy', 'botan']
console.log(nplb.length)
// length に再代入可能。要素は undefined で埋められる
nplb.length = 99
console.log(nplb.length)

// 現在の長さより短くすると超過した分の要素は削除される
nplb.length = 1
console.log(nplb[1])
console.log(nplb)

おわりに

薄い本でも知らなかったことをたくさん知れたので収穫あり。また、ChatGPT に聞きながら読み進めたら本文以外の知識も得られた。 他言語使いからすると奇妙な言語だなと改めて感じた。