JavaScriptに関するまとめです。いきなりプログラミング初心者が読んでもわけ分からないと思います。プログラミングの経験はあるけどJavaScriptははじめてという人にはそこそこ役に立つかもしれません。すでにJavaScriptを勉強したことのある人なら知っていて当然です。なので初心者の人はここに書かれてあることくらいは理解できるように勉強しましょう。
Table of Contents
リテラル構文とシンタックスシュガー
オブジェクト、関数、配列、正規表現に関して
リテラル構文 が
シンタックスシュガー になってます。
関数リテラルは無名関数や匿名関数と呼ばれたりもします。
new String
や
new Number
、
new 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を参照するときには以下のような手順で検索されます;
obj.prop
obj.__proto__.prop
obj.__proto__.__proto__.prop
参照先がObjectオブジェクトのprototypeになるまで検索が続く
そこまでいってなければ検索終了
これがプロトタイプチェインです。ただし__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はレキシカルスコープを採用しています。つまり
関数が実行されるときではなくて、関数が定義されたとき(字句が解析されたとき)に変数のスコープが決まります 。関数実行時には以下の順番でスコープが決定されます;
関数宣言時にスコープチェインが設定される
Callオブジェクトを生成してスコープチェインの先頭に追加
Callオブジェクトを初期化するときにargumentsプロパティをセット
関数の引数、var宣言されたローカル変数をCallオブジェクトに追加
通常の関数の場合の流れは以下の通りです;
関数が入れ子関数を含む場合でも外部から参照されない限り同じような流れになります;
関数が入れ子関数を含み、なおかつ入れ子関数が定義されたスコープの外部から入れ子関数への参照がある場合にはまったく違うことが起こります。これがクロージャです。
クロージャの仕組み
関数が入れ子関数を含んでいて、なおかつ入れ子関数が定義されたスコープの外部から入れ子関数への参照がある場合に、その入れ子関数はクロージャと呼ばれます 。クロージャの典型例はカウンター用の関数です;
closureの参照元を二つつくった場合はCallオブジェクトが異なる別々のスコープチェインがつくられます;
入れ子関数が複数ある場合ではenclosureのCallオブジェクトは共有されます;
参考ページ