JavaScriptに関するまとめです。いきなりプログラミング初心者が読んでもわけ分からないと思います。プログラミングの経験はあるけどJavaScriptははじめてという人にはそこそこ役に立つかもしれません。すでにJavaScriptを勉強したことのある人なら知っていて当然です。なので初心者の人はここに書かれてあることくらいは理解できるように勉強しましょう。

リテラル構文とシンタックスシュガー

オブジェクト、関数、配列、正規表現に関してリテラル構文シンタックスシュガーになってます。 関数リテラルは無名関数や匿名関数と呼ばれたりもします。 new Stringnew Numbernew Boolean はそれぞれの型のリテラル構文と等価ではありません。つまりこれらに関してはリテラル構文はシンタックスシュガーではありません。気になる方は の結果を見てください。

オブジェクトはハッシュテーブルのようなもの

オブジェクトが基本です。オブジェクトはハッシュテーブルのようなもので、ハッシュテーブルのキーがオブジェクトのプロパティ、ハッシュテーブルのキーの値がオブジェクトのプロパティの値に対応します;

関数は第一級オブジェクト

関数は第一級オブジェクトです。ということは値になります。ということはオブジェクトのプロパティの値になれます。つまりオブジェクトのメソッドになります。てことで、JavaScriptでのメソッドはプロパティにセットされた関数を指します。プロパティ(ハッシュテーブルでのキー)はメソッド名になります。コードにするとこんな感じです;

宣言のホイスティング

として実行されます。JavaScriptでは宣言var xのホイスティング(巻き上げ)が起こるからです。ホイスティングは変数のスコープ内で起こります。なので次のようなコードだと関数内のconsole.log(x)ではundefinedが返ってきます; ホイスティングされた後のコードを見るとなぜundefinedになるかは一目瞭然だと思います;

関数宣言と関数式は違う

関数宣言と関数式は違います。例えば次のコード; この二つは決して等価ではありません。シンタックスシュガーでもありません。別物です。関数を実行すれば分かります。まず関数宣言は となってOKですが、関数式の場合は として実行されて undefined is not a function (evaluating 'hoge()') というエラーを吐きます。関数宣言された関数はコンパイル時に定義されますが関数式の関数は実行時に定義されるからです。hoge()を実行する時点で変数hogeは宣言されただけで未定義の状態なので関数として実行できないというわけです。関数式はホイスティングされないために関数宣言と関数式では振る舞いが異なります。

値渡しと参照渡し

プリミティブ型(文字列型、数値型、論理値、null、undefinedの5つ)は値渡し、オブジェクト型(プリミティブ型以外)は参照渡しです。参照渡しだと次のような振る舞いをします。 ここではこんな感じのことが起こっています;

クラスはあるの?

ありません。仕様です。 ES2015でJava Scriptにもクラス構文が導入されましたが、これはあくまでもプロトタイプベースでのオブジェクト指向のシンタックスシュガーであって、言語レベルで新たにクラスが実装されたわけではないです。

プロパティの検索にはプロトタイプチェインが使われる

(例外を除いて)オブジェクトは生成されると__proto__プロパティをもちます。__proto__には別のオブジェクトへの参照が入ります。あるオブジェクトobjが、あるプロパティpropを参照するときには以下のような手順で検索されます;
  1. obj.prop
  2. obj.__proto__.prop
  3. obj.__proto__.__proto__.prop
  4. 参照先がObjectオブジェクトのprototypeになるまで検索が続く
  5. そこまでいってなければ検索終了
これがプロトタイプチェインです。ただし__proto__は標準仕様ではありません。

コンストラクタ関数からnew演算子でオブジェクトを生成したときにおこなわれること

var obj = new FuncObject();としたときの挙動です; つまりnew演算子によって、生成されたオブジェクトの__proto__プロパティにコンストラクタ関数のprototypeプロパティが参照しているオブジェクトが代入されます。これにプロトタイプチェインの仕組みが合わさることで、プロトタイプ(原型)となるオブジェクトをベースにして新しいオブジェクトが生成されます。JavaScriptが『プロトタイプベースのオブジェクト指向プログラミング言語』と呼ばれるのはこのためです。

グローバルスコープとローカルスコープ

JavaScriptは関数スコープです。関数が変数のスコープを決定し、各関数の中がローカルスコープになります。ローカルスコープで宣言された変数はその関数の内側からだけ参照できます。つまり『外側から内側は見えない』けど『内側から外側は見れる』ようになっています。 変数のスコープがグローバルスコープの場合はプログラム全体からアクセスすることができます。varをつけて宣言しないとどこにいようが変数のスコープはグローバルスコープになります。たとえ関数の中でもvarをつけないとグローバル変数になります。scriptタグの直下で定義された変数もグローバル変数になります。ローカル変数は関数の実行終了時に、グローバル変数はプログラムの実行終了時に破棄されます。 関数の引数は、もしそれが値渡しであるプリミティブ型であればローカル変数になります。でもオブジェクトや配列のような参照渡しであればローカルスコープから外れます。つまり関数内外から同じ実体にアクセスすることができます。 JavaScriptにブロックスコープはありません。forやifの中の”{}”に変数が定義されていても、その変数のスコープを決定するのは変数が含まれている関数です。

スコープチェインは変数オブジェクトのリスト

変数の宣言と参照は、変数名をプロパティ、変数値を値とした変数オブジェクトへの読み書きに他なりません。グローバルスコープにおける変数オブジェクトはグローバルオブジェクトです。ローカルスコープにおける変数オブジェクトはCallオブジェクト/activationオブジェクトです。 Callオブジェクトは関数の呼び出し時に生成されて直接アクセスすることはできません。Callオブジェクトにはthis、argumentsオブジェクト、変数に渡された引数、ローカル変数、親のCallオブジェクトへの参照がセットされます。関数の実行が完了するとCallオブジェクトは破棄されます。 変数の参照はCallオブジェクトをたどっていくことで行われます。検索はグローバルオブジェクトにたどりついたときに終了します。変数の名前解決に使われるこの仕組みがスコープチェインで、プロトタイプチェインとまったく同じ仕組みになっています。

レキシカルスコープとCallオブジェクト

JavaScriptはレキシカルスコープを採用しています。つまり関数が実行されるときではなくて、関数が定義されたとき(字句が解析されたとき)に変数のスコープが決まります。関数実行時には以下の順番でスコープが決定されます;
  1. 関数宣言時にスコープチェインが設定される
  2. Callオブジェクトを生成してスコープチェインの先頭に追加
  3. Callオブジェクトを初期化するときにargumentsプロパティをセット
  4. 関数の引数、var宣言されたローカル変数をCallオブジェクトに追加
通常の関数の場合の流れは以下の通りです; 関数が入れ子関数を含む場合でも外部から参照されない限り同じような流れになります; 関数が入れ子関数を含み、なおかつ入れ子関数が定義されたスコープの外部から入れ子関数への参照がある場合にはまったく違うことが起こります。これがクロージャです。

クロージャの仕組み

関数が入れ子関数を含んでいて、なおかつ入れ子関数が定義されたスコープの外部から入れ子関数への参照がある場合に、その入れ子関数はクロージャと呼ばれます。クロージャの典型例はカウンター用の関数です; closureの参照元を二つつくった場合はCallオブジェクトが異なる別々のスコープチェインがつくられます; 入れ子関数が複数ある場合ではenclosureのCallオブジェクトは共有されます;

参考ページ