『超速! Webページ速度改善ガイド』を読んだ

超速! Webページ速度改善ガイド ── 使いやすさは「速さ」から始まる (WEB+DB PRESS plus)

ここ最近、仕事でWebサイトのパフォーマンス改善をしているので、最新のベストプラクティスを押さえるために読みました。

パフォーマンス改善については、『ハイパフォーマンスWebサイト』という古典的名著があり、ここ最近の本としては『Webフロントエンド ハイパフォーマンスチューニング』などがあります。

パフォーマンス改善には、計測(ボトルネックの特定)=>改善というステップが存在します。本書は、計測方法や改善の方策が、具体的にわかりやすく解説されているのが特徴です。また、章立てが 基礎知識 => 調査と改善 で統一されているため、効率よく知識を身につけることができます。

Chrome DevTools はパフォーマンスの改善には欠かせないツールですが、高機能なぶんどのような機能があるか把握するのが大変です。本書では、ネットワーク・スクリプティング・ペインティング・メモリ等、様々なシーンでDevToolsを使って計測をする方法が解説されています。

また、改善方法も、小さなサンプルコードが載っているため、すぐに導入しやすいです。

Webサイトのパフォーマンスに悩んでいる人が、最初に読む本としてオススメ。


『React入門』を読んだ

React入門 React・Reduxの導入からサーバサイドレンダリングによるUXの向上まで (NEXT ONE)

『React入門』は、JavaScriptの定番ライブラリ、React.jsの入門書です。

Reactの入門書は他にもありますが、本書が特に優れているのは、実務で使う上で必要なポイントが書かれている点です。

Reactはそれほど機能の多くないビューライブラリで、Reactと他のライブラリを適宜組み合わせることで、リッチなユーザインターフェースを実現します。

しかし、従来の入門書の多くは、React本体の入門で終わっています。ある程度の規模になると必要になる状態管理ライブラリや、リッチなインタフェースを実現するためのUIライブラリ等、Reactは本体以外にも学ぶべきことが色々あります。

本書では、他の入門書で扱っているようなReact本体の入門は前半3分の1くらいで、その後はReduxやUIライブラリ、ユニットテスト、作ったアプリケーションの公開方法(GitHub PagesとFirebase)からサーバサイドレンダリングまで、より実践的なトピックを手厚く紹介しています。

Vue.jsやAngular等、モダンなJavaScriptフレームワークの経験者であれば、本書でいきなりReactに入門できると思います。そうでない人は、もう1冊他の入門書を挟んでから本書を読んだ方が良いかもしれません。

Webフロントエンド技術を扱った本の宿命として、本書もそれほど賞味期限は長くないので、早めに読むのがオススメです。

Learning Vue.js 2 を読んだ

Learning Vue.js 2

Vue.jsは公式ドキュメントが充実していて、かつ、完璧な日本語訳もされているので、Vue.jsの入門書はそれほど必要ではありません。また、vue-cliが生成するアプリケーションの構造を見ると、ライブラリの組み合わせ方や設定方法などもわかります。

しかし、実際のアプリケーションを作りながらステップバイステップで作るチュートリアルは公式では提供されていません。ブログなどに書かれた簡単なチュートリアルは見つかりますが、それなりの規模のアプリケーションをテストまで含めて書いていく、というものになると、数は少ないでしょう。

本書『Learning Vue.js 2』はそのようなユースケースにぴったりな本です。本書の基本的な構成は、Vue.jsを触り始めるところから始めて、vue-cliによるスキャッフォルディング、ディレクティブの解説、コンポーネント、プラグイン、テスト、デプロイと、Vue.jsアプリケーションを作るのに必要な事項が一通り網羅されています。これらの事項を、「ショッピングリスト」と「ポモドーロタイマー」の2つのアプリケーションを作る過程で学ぶことができます。

英語で書かれた本ではありますが、使われている語彙や文法はとても易しいです。著者がドイツ人で、おそらく英語ネイティブではない、というのが良い具合に作用していそうです。Vue.jsの入門書としてだけでなく、英語で書かれた技術書にチャレンジしてみたい方にもおすすめです。

Secrets of the JavaScript Ninja 2nd 読書メモ 第5章 達人のための関数:クロージャとスコープ

Secrets of the Javascript Ninja

  • クロージャによる開発の単純化
  • 実行コンテキストでJavaScriptプログラムの実行を追跡する
  • レキシカル環境で変数スコープを追跡する
  • 変数の型を理解する
  • クロージャの動作を調べる

クロージャを理解する

JavaScriptを書く上で、クロージャは欠かせないものである。クロージャを使うと、クロージャを定義した時点において、同一スコープに属するクロージャの外部の変数を操作することができる。

最も基本的なクロージャは以下のようになる。下記サンプルで、outerFunction関数は、outerValueにアクセスできる。

クロージャの働きがわかりやすい例は以下のようになる。

outerFunctionの呼び出しによって値がセットされたinnerValueは、呼び出しの終了によってメモリ上から消えてしまい、最後のlater()によるinnerFunctionの呼び出しの時点ではundefinedになっているようにも思える。
しかし、実際にlater()を実行すると、innerValueには’ninja’が入っている。

実は、innerFunctionを定義するとき、関数宣言を定義するだけでなく、関数定義の時点のスコープ内にある全ての変数を包含するクロージャが作成されているのだ。ここで重要なのは、クロージャ経由で外部の変数にアクセスする場合、その変数はクロージャが破棄されるまでメモリ上に残り続けるという点である。

クロージャを動かす

プライベート変数(のようなもの)

JavaScriptにはプライベート変数はないが、クロージャを使うことで似たようなものを作り出すことができる。

クロージャをコールバックとして使う

実行コンテキストによるコード実行の追跡

JavaScriptにおいて、実行の基本的な単位は関数である。JavaScriptのコードは、関数が関数を呼び、そして呼び出し元に戻っていくような流れで実行される。このとき、JavaScriptエンジンは関数呼び出しのスタックを記憶している。

コードがJavaScriptエンジンによって実行されるとき、それぞれの文は一定の実行コンテキストによって実行される。実行コンテキストには、グローバルコンテキストと関数実行コンテキストの2種類がある。

  • グローバルコンテキストは1つだけ存在する
  • 関数実行コンテキストは関数呼び出しのたびに作成される

関数呼び出しのたびに実行コンテキストが作成されるが、その関数の処理が終わると元の場所に復帰する必要がある。そのため、関数内でさらに関数が呼ばれるような場合には、関数の実行コンテキストがスタックとしてメモリ上に保持される。

関数の実行スタック(Call Stack)は、Chrome DevTools等を活用することで簡単に確認できる。

レキシカル環境(lexical environments)で識別子を追いかける

レキシカル環境(lexical environments)とは、JavaScriptエンジンの内部の構成要素で、識別子と変数のマッピングを追いかけるのに使われる。

上記コードで、レキシカル環境は、console.log文の際にninja変数について尋ねられる。レキシカル環境は、一般にはスコープと呼ばれる。

通常、レキシカル環境はJavaScriptコードの特定の構造と結びついている。すなわち、関数、コードのブロック、try-catch文のcatch部分である。これらの構造は独自の識別子マッピングを持つことができる。

コードのネスト

レキシカル環境は、多くの場合、コードのネストに基づいている。

skulk関数を呼び出すたびに、新しいレキシカル環境が作成される。

重要なのは、内側のコードは外側でアクセスされた変数にアクセス可能なことである。

コードのネストとレキシカル環境

ローカル変数、関数宣言、関数パラメーターの追跡に加えて、それぞれのレキシカル環境は外部のレキシカル環境も追跡しなければならない。JavaScriptでは、関数をファーストクラスオブジェクトにすることで、外部の環境の追跡を可能にしている。

関数が作成されると、関数が作成されたレキシカル環境への参照が、 [[Environment]] という内部プロパティとして保持される。二重の角カッコは、JavaScriptエンジンが内部で使用するプロパティの印であり、プログラムから直接触ることはできない。

関数が実行されるたびに、新しい関数の実行コンテキストが作成され、スタックに積まれる。加えて、新しく関連するレキシカル環境が作成される。新しく作成されるレキシカル環境の外部環境のために、JavaScriptエンジンは関数の内部の [[Environment]] プロパティから環境を取り出す。これによって、新しく実行される関数のための環境が用意される。

JavaScript変数の種類を理解する

JavaScriptでは、3種類のキーワードを使って変数を定義できる。var, let, constである。これらは (1) 変更可能性(mutability) (2) レキシカル環境との関係 という2点で異なっている。

変数の変更可能性(mutability)

変数宣言のキーワードを変更可能性で分類するなら、constは変更不可、varletは変更可、となる。constで宣言された変数は変更不可(immutable)となり、値の再代入ができない。一方、varまたはletによって宣言された変数は何度でも値を再代入することができる。

const変数

const 変数には、宣言と同時に初期値を設定する必要がある。const 変数は以下のような目的で使用される。

  • 再代入すべきでない変数を明示する
  • 固定された値を定義する

const 変数には再代入ができないため、意図しない変更から保護されている。また、JavaScriptエンジンによる最適化も可能になる。再代入の不要な変数(多くは不要なはず)に対しては、constを使って宣言することが推奨される。

なお、constで宣言した変数は再代入ができないだけで、オブジェクトのプロパティを変更したり、配列の要素を追加・削除したりすることは可能である。オブジェクトや配列の変更を禁止したい場合はObject.freezeメソッドを使用する。

変数定義キーワードとレキシカル環境

レキシカル環境との関係(スコープ)の観点で分類するなら、varlet, constの間で線が引かれる。

varキーワードの使用

varキーワードを使用するとき、変数は最寄りの関数かグローバルなレキシカル環境に保存される(ブロックのレキシカル環境は無視されることに注意!)。

let, constを使ってブロックスコープ変数を定義する

let, const を使って定義した変数は最寄りのレキシカル環境に保存される。もし最寄りがブロックのレキシカル環境であれば、そこに保存される。

再代入可能な変数には let を使うことで、よりスコープの狭い変数を定義することができる。後方互換性以外の理由でvarを使う理由はない。

レキシカル環境に識別子を登録する

識別子の登録過程

JavaScriptコードの実行は2段階に分けて行われる。

最初のフェーズは新しいレキシカル環境が作成されるたびに実行される。このフェーズでは、コードは実行されず、JavaScriptエンジンは変数と関数の宣言を探して現在のレキシカル環境に登録する。2番目のフェーズでは、コードが実行される。このフェーズは以下のように詳細化できる。

  1. 関数環境を作成する場合、arguments識別子を作成する
  2. 関数またはグローバル環境を作成する場合、コードを走査して関数宣言を探し、識別子を登録する
  3. 変数宣言のための走査が行われ、varで宣言された変数は最寄りの関数の、letまたはconstで宣言された変数は最寄りのブロックのレキシカル環境に登録される

関数宣言より前に関数を実行する

  • 関数宣言を使用して定義された関数には宣言より前でもアクセスできる
  • 関数式やアロー関数はできない

関数の上書き

関数宣言を使用すると、その関数を参照する変数はコードの実行よりも先に定義される。

まとめ

  • クロージャによって、その関数が定義されたスコープ内にある全ての変数にアクセス可能になる
  • クロージャは疑似プライベート変数やコールバック関数に利用できる
  • JavaScriptエンジンは実行スタックによって関数の実行を追跡する
  • JaaScriptエンジンはレキシカル環境(スコープ)によって識別子を追跡する
  • JavaScriptでは、グローバル・関数・ブロックのいずれかのスコープの変数が定義できる
  • 変数の定義には var, let, const キーワードが使用できる
  • クロージャはJavaScriptのスコープのルールの副作用である

Secrets of the JavaScript Ninja 2nd 読書メモ 第4章 関数呼び出しを理解する

Secrets of the Javascript Ninja

暗黙的な関数パラメータの使用

関数には、暗黙的に渡されるパラメータが2つある。argumentsthisである。

argumentsパラメータ

argumentsパラメータは関数への引数の集合である。argumentsを使えば明示的に定義されていないパラメータも受け取ることができる。これを利用すると、関数オーバーロードを実現できる。

ES2015のrestパラメータを使用すれば、argumentsが必要になる場面は減少するが、古いコードに触るときのためにもargumentsの挙動は理解しておいたほうがよい。
argumentsにはlengthプロパティがあり、引数の数を調べることができる。また、argumentsからは配列アクセスの形式で値を取り出せる。

argumentsは配列のようだが、配列ではない。そのため、sort等の配列操作用メソッドを使うことはできない。配列のように扱いたい場合は、ES2015のArray.fromメソッド等を使用して配列に変換する必要がある。

thisパラメータ

thisは関数呼び出しのコンテキストを保持するパラメータである。JavaやC#のようなオブジェクト指向言語とは異なり、JavaScriptのthisの値はどのように呼び出されたかによって異なる。JavaScriptのオブジェクト指向の理解にあたっては、thisは最も重要な部分のひとつである。

関数呼び出し

関数呼び出しにはいくつかのパターンがある。

  • 通常の関数: skulk()
  • メソッド: ninja.skulk()
  • コンストラクタ: new Ninja()
  • applyまたはcallによる呼び出し: skulk.call(ninja) または skulk.apply(ninja)

関数としての呼び出し

関数としての呼び出しでは、thisの指す値はstrictモードか否かによって異なる。

メソッドとしての呼び出し

関数がオブジェクトのプロパティとして割り当てられている場合、この関数をオブジェクトのメソッドと呼ぶ。この場合、thisパラメータが指すのは、メソッド呼び出しの対象となったオブジェクトである。

コンストラクタとしての呼び出し

コンストラクタとは、オブジェクトを組み立てる関数である。コンストラクタを使うことで、同様の機能を持つオブジェクトを簡単に作ることができる。

関数がnew演算子とともに呼び出されると(コンストラクタとしての呼び出し)、空のオブジェクトがthisに割り当てられる。このオブジェクトはnew演算子の結果として返る。

applyとcallによる呼び出し

イベントハンドラに紐付けた関数において、thisはイベントが発生したオブジェクトを指す。このように、関数のコンテキストを呼び出し側から操作する方法がapplycallである。

関数のapplyメソッドを使って呼び出しを行う場合、第1引数に関数コンテキストとして用いるオブジェクト、第2引数に呼び出し時の引数の配列を渡す。callメソッドも同様だが、引数は配列ではなくパラメータリストに直接渡す。

関数コンテキストの問題を修正する

関数コンテキストを操作する方法には、call/applyの他にアロー関数式とbindメソッドがある。

アロー関数式による関数コンテキストの回避

アロー関数式は、みずからのthisをもたない。代わりに、その関数が宣言された時点のthisパラメータの値を保持している。
イベントハンドラとして関数を登録しても、thisの値は変わらない。

bindメソッドの使用

関数のbindメソッドを使用すると、その関数が実行されるコンテキストを呼び出し方法に関わらず変更できる。bindメソッドは、別の関数コンテキストを持った別の関数を返す関数である。