はじめに
クロージャって何?
クロージャとは、関数内に現れる変数の名前解決が、ローカルスコープだけでなく、関数が定義された場所のスコープも含めて行われる関数のことです。
Perfect PHP
関数の中に関数を書く
クロージャを理解するために、関数の中の関数を使うコードを書いてみます。まずは関数リテラルをそのままreturnする方法。
function f() {
return function() {
console.log("Hello!");
return;
};
}
var fn = f();
fn(); //Hello!
fn(); //Hello!
あるいは、こんな風にも書けます。関数リテラル式を変数に入れてからreturnする方法。
function f2() {
var inner = function () {
console.log("Yo!");
return;
};
return inner;
}
var fn2 = f2();
fn2(); //Yo!
fn2(); //Yo!
こんな風に書き直すこともできます。関数を一旦定義してから、関数名をreturnする方法。
function f3() {
function inner() {
console.log("Hi!");
return;
};
return inner;
}
var fn3 = f3();
fn3(); //Hi!
fn3(); //Hi!
クロージャの例
Perfect JavaScriptのクロージャのサンプルコードです。
function f() {
var cnt = 0;
var inner = function() {
return ++cnt;
};
return inner;
}
var fn = f();
console.log(fn()); //1
console.log(fn()); //2
console.log(fn()); //3
cntは関数fの外からは参照できないですが、クロージャ(inner)からは参照することができます。ここまで来ると、冒頭の「クロージャって何?」の意味が分かる気がします。
なお、ここではinner_func2.jsの形式を使って関数を定義しましたが、他の形式でも同様の動作をさせることができます。
二つの関数を入れる
関数fの中に、2つのクロージャを入れることもできます。
function f() {
var cnt = 0;
var add = function() {
return ++cnt;
};
var sub = function() {
return --cnt;
};
return {add:add, sub:sub};
}
var fn = f();
console.log(fn.add());
console.log(fn.add());
console.log(fn.sub());
console.log(fn.sub());
2つのクロージャから、同じ変数cntを参照できます。
Classっぽく書く
thisを使って、クラスっぽく書くこともできます。
function MyClass() {
this.cnt = 0;
this.add = function() {
return ++this.cnt;
};
this.sub = function() {
return --this.cnt;
};
this.show = function() {
console.log(this.cnt);
};
return this;
}
var fn = MyClass();
console.log(fn.add()); //1
console.log(fn.add()); //2
console.log(fn.sub()); //1
console.log(fn.sub()); //0
// 直接操作できちゃう
fn.cnt = 10;
fn.show(); //10
fn.add();
fn.show(); //11
MyClass内の関数から参照する変数を、this.cntとすれば外部から直接参照可能になります。次のようにローカル変数(var cntで宣言)とすれば外部からは直接参照不可になります。
function MyClass() {
var cnt = 0;
this.add = function() {
return ++cnt;
};
this.sub = function() {
return --cnt;
};
this.show = function() {
console.log(cnt);
};
return this;
}
var fn = MyClass();
console.log(fn.add()); //1
console.log(fn.add()); //2
console.log(fn.sub()); //1
console.log(fn.sub()); //0
// 直接操作できない
//fn.cnt = 10;
fn.show(); //0
fn.add();
fn.show(); //1
初期値を設定する
fの引数を利用するなどして、クロージャから参照する変数の初期値を与えることもできます。
function f(cnt_init) {
var cnt = cnt_init;
var add = function() {
return ++cnt;
};
var sub = function() {
return --cnt;
};
return {add:add, sub:sub};
}
var fn = f(10);
console.log(fn.add()); //11
console.log(fn.add()); //12
console.log(fn.sub()); //11
console.log(fn.sub()); //10