結論(まとめ)
たわ言はいいから答えを教えろって輩のために結論から話すと
クロージャー(Closure)とは無名関数としても定義できることから
つまりは関数です。
もう一度言います。
関数です。
はい、僕も一緒に理解しました。
普通にこんな関数見たことあると思いますが、
func closure(){
print("Closureってなんじゃい!!")
}
こいつと同じ仲間の関数なんです。
「だったら"クロージャー"じゃなくて、普通の関数を使えばいいじゃん!」って思いますよね。それでは一緒になんのための「クロージャー」が存在するのか見てみましょう。
クロージャはコードの簡略化がメインの目的(基本的には)
冒頭でもお伝えしたようにクロージャーとは無名関数なのでわざわざ関数に名前をつけてあげる必要がありません。
したがって定義の仕方もこんな感じになります。
特に戻り値の肩を指定しないやり方もあるので後述します。
{ (引数名:引数の型) -> 戻り値の型 in
処理
}
一般的なやつはこんな感じ。
func 関数名(){
処理
}
でもこんなの見たところで「コードの簡略化」というクロージャーの持ち味を理解するのは難しいですよね。
大切なのは「〇〇ができないよ〜ドラえもん」ってときに「それじゃクロージャーを使いな」って言えるぐらいのcoolな理解。
自分としてはjavascriptの方が理解しやすいので、
まずはjavascriptで説明します。
// 普通の関数を定義する
function counter() {
var n = 0;
// クロージャ(関数)を作成して返す
return closure() {
return n++;
};
}
// 戻り値 (関数オブジェクト) を変数に格納しておく
var counter = createCounter();
alert(counter()); // 0が表示される
alert(counter()); // 1が表示される
alert(counter()); // 2が表示される
alert(counter()); // 3が表示される
alert(counter()); // 4が表示される
この関数の中で定義されているclosure関数が「クロージャ-」です。
What does it mean to “close over” something?(“close over”ってドユコトデスカ?)
stack overflowでも 「クロージャ-」って何よ!わかんないよ!
っていう質問があったので見てみるとjavascriptで書いたクロージャの意味が腹落するかと思います。
What's a closure? "Oh, it's a function that closes over another function."
翻訳すると、
closureって何よ? "あー、他の関数を囲い込んで覆う関数よ"
まだ ?って人のために大切なポイントをjavascriptのコードで説明すると、
function counter() {
var n = 0;
変数nを
return closure() {
return n++;
};
}
closure( )で 囲んで襲っている(使用している)。
値を囲って襲う(使用する)から"クロージャー"
ここまでくると理解が深まってきた気がします。
クロージャーの意味や必要性を調べていると
クロージャ(クロージャー、英語: closure)、関数閉包はプログラミング言語における関数オブジェクトの一種。いくつかの言語ではラムダ式や無名関数で実現している。引数以外の変数を実行時の環境ではなく、自身が定義された環境(静的スコープ)において解決することを特徴とする。
こんな難しい日本語ばかりで理解に苦しみました。
少しだけ関数閉包のニュアンスが理解できた気がします。
念のために
クロージャーにもいくつか種類というか、色々な記述の仕方があります。
例えば、引数があるやつ、ないやつ、
@shiracamus さんから貴重なご意見を頂いたのでシェアします。
以下のようなコードがあります。
let closure = { () -> Void in print("クロージャテスト") }
// クロージャ実行
closure()
これはクロージャーではありません!
クロージャーとは、
引数以外の変数を実行時の環境ではなく、自身が定義された環境(静的スコープ)において解決することを特徴とする。
by クロージャ 出典: フリー百科事典『ウィキペディア(Wikipedia)』
ん????? になった人のためにも静的スコープからご説明します。
静的スコープは別名「レキシカルスコープ」と言います。
レキシカルスコープからの引用になりますがメチャクチャ分かり易かった。
以下のコードで説明すると、
var x = 10;
function A(){
console.log(x); //この時の静的なスコープはx=10
}
function B(){
var x = 1000; //ここでもxが定義されている
A(); //この時のxは10?1000?
}
A(); //10
B(); //10
レキシカルスコープは 関数を定義した時点でスコープが決まります。
ふむ。
ちなみに上記コードの静的スコープは「var x = 10;」です。
上記のコードを実行すると関数Aも関数Bも10の値を返すことがわかります。
つまり、呼び出し元の関数Bで新たにxが定義されていようとも、関数Aが定義された時点で参照していたxが出力されます。
みなさん静的スコープがご理解いただけたでしょうか?
とりあえずわかったつもりで一旦本題のクロージャーに戻りましょう。
以下のコードをご覧ください。
function A(){
var count = 0;
var countup = function B(){ //countup()だけがcountを操作できる
count++;
return count;
};
return countup;
}
var testCountup = A();
console.log(testCountup());
console.log(testCountup());
console.log(testCountup());
①関数Aの中で定義された関数Bがある。
②このとき関数Bからは関数A内で定義された変数Xにアクセスすることができる。
③関数Aの実行が終わってしまった後でも何らかの方法で関数Bを実行できた場合、関数Bだけが変数Xを操作することができます。
④関数Bだけが変数Xを操作できるのでクロージャ(閉包)と呼ばれる。
これがwikipedia先生がおっしゃる、
引数以外の変数を実行時の環境ではなく、自身が定義された環境(静的スコープ)において解決することを特徴とする。
ってことなんですね。
@shiracamus さんの言葉をお借りすると、
function Bが外側にあった変数countを包み隠して持ち歩いてる。
そういうことです。(震え声)
まとめ
きっと使いこなすには「コールバック処理」とかも理解する必要があると思います。
僕も勉強中なのでまたまとめます。
この記事は先人の知恵、海外の記事を参考にしました。
使い方もさることながら、語源を知ることが理解が早いような気がします。
@mochizukikotaro さんありがとうございます。