37
40

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

久しぶりにJavaScriptを思い出す(関数オブジェクト、クロージャ)

Last updated at Posted at 2016-06-26

動機と目的

Node.js, JavaScriptを久しぶりに使おうとして、さっぱり忘れていることに気づいた。なので、ちょっと思い出すために調べたことをメモ代わりに残す。(といった使い方も Qiita は良いのだろうか?)

当面の目的は以下のTimerを使えるようになること。
Node.js API - JavaScript のタイマー機能と非同期呼び出し | プログラマーズ雑記帳

最初にクロージャというのがわからなかった。以下の記事に解説されているが、
JavaScriptでクロージャ入門。関数はすべてクロージャ? - Qiita

そもそも以下のサンプルが読めない(読み解くことができない)。

var module = (function() {
  var count = 0;

  return {
    increment: function() {
      count++;
    },
    show: function() {
      console.log(count);
    }
  };

})();

module.show(); // 0

module.increment();
module.show(); // 1

console.log(count); // undefined

そういえば無名関数というのがあったのだな、とぼんやりと思い出す。

最初のvar moduleの定義の最後に })(); とやたらカッコが書かれているのが、なんのことか分からない。最初の}は、function() { を閉じるためのカッコ、次の)は module = ( を閉じるためのカッコである。しかし、その次の()というのが何だ?、と思った。

で、調べてみて、分かったことを以下に記述する。

関数の受け渡し、定義即実行、プロパティ、クロージャなど

まず、JavaScriptでは関数を、値と同じように受け渡しできる。

function f1() {};
var a = f1;

最初のはf1という関数の定義で、何もやらない関数である。この関数をaに代入している。こういうことが可能である。これを短く書くと、以下となる。

var a = function f1() {};

さらに、この例では関数f1は、aとして呼び出されるため、f1という名前は無駄でもある。よって、以下のように関数名すら定義から消すことができる。

var a = function () {};

さて、では関数が代入されたaを実行するには、どうすれば良いのか、というと、以下のようにする。

var a = function () {};
a();

aというのは = 以下の、無名の関数への「参照」であるから、実行するには()を付ける。動作確認するために、出力を加えてみる。

var a = function () { console.log("TEST"); };
a();

ここで、関数の定義の後ろに()をつければ、定義した直後に実行できる。これが良く使われる。つまり、関数の定義と実行を一括で記述する簡便な方法だ。

var a = function () { console.log("TEST"); } ();

こうすると、実行された結果がaに代入される。この関数は何もreturnしないので、aには何も代入されない。

var a = function () { console.log("TEST"); } ();
console.log(a);

上記のコードを実行すると、TESTの後に、「undefined」が出力される。aには何も入っていない、という意味である。

以下のように関数の中で returnを定義すれば。aには実行結果として、1が代入されることになる。

var a = function () {
  return 1;
} ();

console.log(a);

最初に述べたように、JavaScriptでは関数を値と同じように受け渡しできるため、この戻り値も関数にすることができる。

var a = function () {
  return function () {
    console.log("TEST");
  }
} ();

こうすると、aには、関数を実行した結果、return の後ろの関数がaに代入されることになる。よって、以下のように戻ってきた関数を実行することができる。

var a = function () {
  return function () {
    console.log("TEST");
  }
} ();

a();

最初のコードを理解するには、さらにプロパティを知る必要がある。return文の中に、プロパティが定義されている。return文に2つのプロパティを入れると、例えば以下のようになる。

var a = function () {
    return {
        propA: function () { console.log("A"); },
        propB: function () { console.log("B"); }
    };
} ();

a.propA();
a.propB();

aはpropAと、propBという2つのプロパティを持つ関数である。それぞれのプロパティには、別の関数が割り当てられている。

ここまで理解できると、クロージャを解説している最初のプログラムがわかるようになる。

var module = (function() {
  var count = 0;

  return {
    increment: function() {
      count++;
    },
    show: function() {
      console.log(count);
    }
  };

})();

module.show(); // 0
module.increment();
module.show(); // 1

// console.log(count); // undefined

ちなみに、module = (の(は不要である。 ここで不思議がらねばならないのは、countという変数の値が、保持されている点である。関数の中で定義された関数は、その属しているスコープの変数も一緒に保持するようになっている。

moduleという関数は、countの値も含めて更新されている。これがクロージャである。値も含めて保持していることを使うと、次のようなプログラムを書くこともできる。

function counter() {
    var count = 0;

    return {
        increment: function() { count++;
    },
        show: function() {
            console.log(count);
        }
    };
};

var mod1 = counter();
var mod2 = counter();

mod1.show(); // 0                                                                                      
mod1.increment();
mod1.show(); // 1                                                                                      

mod2.show(); // 0                                                                                      
mod2.increment();
mod2.show(); // 1          

counterの実行結果、つまりreturnの後ろの関数群を mod1 と mod2 という2つの変数に代入する。
それぞれ代入時点で、var count = 0でcountが初期化される。

その後、incrementが呼ばれるたびに内部に保持しているcountが+1される。
(mod1がオブジェクト、その属性値がcountという感じになる。)

外から直接操作できない count は、まるでプライベート変数のようである。
(ちなみに、JavaScriptにはプライベート変数は無いらしい)

なるほど。

まとめ

  • JavasScriptでは、関数も代入したり、引数や、返り値に使うことができる。

  • 関数を定義した直後に実行することもできる。

  • 実行オプション付きの関数を、変数に代入すると、実行結果のreturnの中身が代入される。

  • 関数をreturnすると、関数が代入される。

  • return内にプロパティを定義すれば、プロパティ付きのreturnを返すことができる。

  • 関数内部で定義した関数を、代入した変数(オブジェクト)では、中の変数の値も保持し続ける。

  • この変数値も保持し続ける関数をクロージャと呼ぶ。

  • クロージャを使うとプライベート変数のようなものが実現できる。

  • JavaScriptは、スクリプト言語としての便利さを追求したものだと分かった。

  • あと、Qiitaは、プログラムを記述するのに、とても便利だと分かった。

37
40
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
37
40

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?