読者です 読者をやめる 読者になる 読者になる

console.lealog();

@leader22のWeb系に関する勉強めもブログですのだ

JavaScriptのクロージャについて

独学でプログラミングを学ぶにあたって一番困るのは、本やネットのサンプルを一体どの場面でどんな風に使うかが全くわからない点かと思ってます。

何が嬉しくて「動物クラスを作ってワンワンする」のか。
何のために「フィボナッチ数を計算する」関数を作るのか・・。

「仕組みとしてこういうものがある」というのはわかるものの、具体例が具体的じゃないせいで理解できない。
そして、独学なので質問できる相手がいない。
以下略。

という葛藤の中、個人的に理解に苦しんでいる「クロージャ」についての理解の現状をメモ。
単語ばっかりよく聞くけど、今までずっと正体不明だったので。

はじめに

いきなりの結論

はじめに、いろいろ調べまくった結果、一番わかりやすかった記事を。

参考:Why use "closure"? - How To Node - NodeJS

英語やけども、一番腑に落ちました。

どうやらクロージャとは、「状態を保持」する関数のことらしい。

原文:
A closure is a function defined within another scope
that has access to all the variables within the outer scope.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
意訳:
クロージャは、別のスコープを内包した関数である。
内包されたスコープからは、クロージャ内の全ての変数にアクセスできる。

状態を保持?

var countUp = function(){
  var i = 1;
  return function(){
    alert(i);
    i++;
  };
};

var func = countUp();

func(); // 1
func(); // 2
func(); // 3

funcにカウントが保持されて、呼び出す度に数が増えていく。
関数に関数を内包することによって、状態を保持する仕組み。

でもこれって

var i = 1;
var countUp2 = function(){
  alert(i);
  i++;
};

var func = countUp2();

func(); // 1
func(); // 2
func(); // 3

単のカウントアップする関数なら、こういう書き方でも同じことはできますよね。
違うところは色々あるけども。
その違うところが、クロージャの本質であるはず。

クロージャたる所以

参考:JavaScriptクロージャを完全理解!スコープチェインを知る(後編) - page3 - builder

この記事の中に、こう書かれてます。

例えば、「クロージャから、元になる関数内のローカル変数への参照は、確実に存在するけれども『目に見えない』」という特徴は良く利用されます。

ふむ。

function Human(name, age) {
  this.getName = function() {
    return name;
  };

  this.getAge = function() {
    return age;
  };
}

var shiraishi = new Human("shiraishi", 30);

alert(shiraishi.getName() + ":" + shiraishi.getAge()); // shiraishi:30

ふむふむ。
ただこれも、

function Human2(name, age){
  this.name = name;
  this.age = age;
}

var foo = new Human2("foo", 25);

alert(foo.name + ":" + foo.age); // foo:25

違ってくるポイントは・・。

クロージャ:Closure
【不可算名詞】 [具体的には 【可算名詞】] 閉鎖; 締め切り; 閉店,休業; 終止,終結.

「閉鎖された」というのがミソですね。

単純に参照できない

shiraishi.name; // undifined
foo.name; // foo

参照するには、

shiraishi.getName(); // shiraishi

こうすればいけるよね?
否、こうするしかない。

値の書き換えができない

shiraishi.name = "nyaaaa"; // アクセスできないので書き換えできない

foo.name = "nyaaaa"; // 書き換えできちゃう

前者で値を書き換えたい場合は、それ用の関数を用意する必要がある、と。

本来はステートレス

っていう言い回しが正しいかはわからんけどw
本来は関数でも変数でも何でも、自由なタイミングで自由に書き換えたり代入されたりするもの。
RESTみたいにそれ自体が「状態を保持」することは有り得ない中で、クロージャだとそれができる、と。
一旦作成したオブジェクトが、「状態を保持」してるとはこういう意味なのかなーと。

オブジェクト指向のためのクロージャ

こういうのカプセル化って言うんやっけ?
定義した関数なりクラスなり、綺麗なコードを書く(保守性の高いコード、隠蔽されるべき情報が隠蔽されてる・・とか色々)ためのやり方として、クロージャってのがJavaScriptにはある、と。

実際の使い所は?と言われると結局わからないままやけども、そういう感覚は実務で持って知るしかないと思うので、現時点ではどうしようもないか。
ただ、JavaScriptでいうところのクロージャは、オブジェクト指向を実現するためのコアであることは確かで、コードとして「あるべき姿」を実現するためには必須な書き方、ということかなぁ。

次に勉強するもの

ほんとに一応ではあるものの、jQueryやらDOM周りからのJavaScriptはわかってきた気がするので、次はcallとかprototypeとか、プログラミング言語としてのJavaScriptを勉強しよう。