関数
関数宣言文と関数リテラル式の違い
//関数宣言文
function func_name() { /* 処理 */ };
//関数リテラル式
var f = function func_name() { /* 処理 */ };
var f = function () { /* 処理 */ }; // 関数名func_nameは省略可。
関数宣言文は巻き上げ(hoisting ホイスティング:varによる変数宣言と、functionによる関数宣言はスコープ内の一番先頭で"宣言"されたものと見なされる)が起こるため、宣言より前に呼び出してもエラーしない。
引数
関数内から実引数にアクセスするにはarguments
オブジェクトが使える。
arguments.length
として実際に受け取った実引数の数を取得できる。
反対に関数自身の持つlengthプロパティを使うと仮引数の数を取得できる。
スコープ
varにはブロックスコープが存在しない。letではこれがある。
if (var i = 0; i < 2; i++){
}
//ブロックを抜けてもiは2
また、letは以下のようにブロックスコープをもった文や式を作れる。
//let文
let ( x = 1 ) {
// 処理
}
//let式
let ( x = 1 ) 式;
Functionクラス
StringやNumberのように、Function関数・コンストラクタが存在する。
同じく関数宣言文やリテラル表記で定義する方が望ましいらしい。
プロパティ(一部)
- apply() : 第一引数にthis参照先、第二引数に渡す要素のリストを指定して関数実行。
- bind() : 第一引数にthis参照先、第二引数に渡す要素を1つ1つ指定し、クロージャを返す。可変長。
- call() : 第一引数にthis参照先、第二引数に渡す要素を1つ1つ指定して関数実行。可変長。
- length : 関数の仮引数の数(上述)
なお、this参照、クロージャについては下記。
this参照
var obj = {
x: 10,
f: function() {
print this.x;
}
}
var fn = obj.f
fn() // thisの参照先はグローバルオブジェクトとなる。
オブジェクトリテラルとして定義した時にはオブジェクトobjのプロパティであるためthis参照はobjのxを指す。
変数fnにobjの関数オブジェクトfを代入した時、グローバルオブジェクトのプロパティが生成し、fnを呼び出すとthis参照先はグローバルオブジェクトの持つxとなる。
apply関数、call関数は第一引数に指定したオブジェクトがthisの参照先となる。
function f() { print(this.x); }
var obj = { x: 10 };
f.apply(obj);
// 10
クロージャ
表層的な理解:関数スコープを抜けてもローカル変数が生き続け、状態を持つことができる関数。
function f() {
var x = 0;
function g() { return ++x; }
return g;
}
var fn = f();
fn() //1
fn() //2
fn() //3
上記のように関数fのローカル変数が生き続けているように振る舞う。
細かなしくみ。
関数(ここではf)がコールされると、その瞬間Callオブジェクトと呼ばれるものが暗黙裡に生成する。Callオブジェクトは自身のプロパティとして関数スコープの変数や関数を持つ、すなわちここではxやgがCallオブジェクトのプロパティとなる。最後に関数gへの参照がreturnされ、グローバルオブジェクトのプロパティであるfnに格納される。
ここで、本来であればCallオブジェクトはその関数が終了し、どこからも参照されなくなった時点で消滅する(=ガベージコレクション)のだが、プロパティである関数gへの参照がfnに渡っているためCallオブジェクトが消滅しない。
そのため、表面上関数fnが状態を持っているように見える。
クロージャの利用
- グローバル変数の回避
- 変数の隠蔽(privateな変数)
var calc = (function() {
var private_val = 5;
return function (x) {
print private_val * x;
}
}
)();
calc(2); //10
無名の関数は定義と同時に実行され、プロパティである関数への参照がcalcに渡されるため、Callオブジェクトが生き続け、クロージャとなる。
このcalcは関数としてprivate_valにアクセスできるが、ダイレクトにアクセスして値を書き換えたり読み出したりすることはできない。
式クロージャ
Firefoxの独自拡張?deprecatedらしい。詳細は触れない