クロージャのまとめ(1) : JavaScript

Pocket

JavaScriptのクロージャのメモ。

» クロージャのまとめ(2) : JavaScript

関数呼び出し時点における変数名解決の環境を保持した関数

パーフェクトJavaScript (PERFECT SERIES 4)(p177)

JavaScriptは変数名解決の環境をCallオブジェクトを使ったスコープチェーンで実現する。変数名解決の環境を保持しているので下記のように異なる振る舞いができる。

// 普通の関数
// 一つの振る舞いだけ
var count = 0;
function foo() {
    return count++;
}
console.log(foo()); // 0
console.log(foo()); // 1
console.log(foo()); // 2</p>

// クロージャーを使う
function bar() {
    var count = 0;   // 自由変数
    return function() {
        return count++;
    }
}

// 呼び出し元が変数名解決の環境を保持する
// closure1, clousure2は異なる振る舞いする
var closure1 = bar();
var closure2 = bar();
console.log(closure1()); // 0
console.log(closure1()); // 1
console.log(closure2()); // 0
console.log(closure2()); // 1
console.log(closure2()); // 2
console.log(closure1()); // 2

» jsFiddle

レキシカルスコープ(静的スコープ)の面から見たクロージャ

Wikipediaとはてなキーワードはクロージャを次のように記載している。

クロージャ(クロージャー、closure、閉包)はプログラミング言語における関数の一種。引数以外の変数を実行時の環境ではなく、
自身が定義された環境(静的スコープ)において解決することを特徴とする。関数とそれを評価する環境のペアであるともいえる。

» クロージャ – Wikipedia

【closure】閉包。レキシカルクロージャとも言う。
静的スコープ(レキシカルスコープ)を実現するために必要となる。
関数内に出現する自由変数(関数内で宣言されていない変数)の解決の際、実行時の環境ではなく、関数を定義した環境の変数を参照できるようなデータ構造。
「推移的閉包【transitive closure】」とかのクロージャとは全く関係がないのに同じ名前なことが、言語を越えて学習者を無意味に悩ませている

» クロージャとは – はてなキーワード

Wikipediaやはてなキーワードのクロージャについての記述にある

「引数以外の変数を実行時の環境ではなく、自身が定義された環境(静的スコープ)において解決する」
「関数内に出現する自由変数(関数内で宣言されていない変数)の解決の際、実行時の環境ではなく、関数を定義した環境の変数を参照できる」

という説明の、

「環境(静的スコープ)において解決する」
「関数を定義した環境の変数を参照できる」

とは上記のコードのclosure1関数やclosure2関数 – bar関数内で定義した関数 – が実行される時に参照する変数countは、
(実行時の)グローバル変数countではなくbar関数のローカル変数countを参照することを言う。
定義したときにスコープが決まるという意味で静的スコープと呼ぶ[1]

クロージャが静的スコープであることからclosure1関数やclosure2関数-bar関数内で定義した関数-は自身が定義されたbarのコンテキストへアクセスできる。 注目すべき点として包含するbar関数が処理を終えた後でもclosure1関数やclosure2関数は静的スコープなのでbar関数の変数countへアクセスし続けることができる。

クロージャを使ったイベントハンドラ

<div id="foo">Paragraph foo Click Me</div>
<div id="bar">Paragraph bar Click Me</div>
var foo = document.getElementById('foo');
[javascript]
foo.onclick = function() {
    var flag = false;
    return function () {
        if (flag) {
            alert('Even Click');
            flag = false;
        } else {
            alert('Odd Click');
            flag = true;
        }
    }
}();

可読性を良くする為に括弧をつけると良い。

elm.onclick = (function() {
    var flag = false;
    function f() {
        if (flag) {
            alert('Even Click');
            flag = false;
        } else {
            alert('Odd Click');
            flag = true;
        }
    };
    return f;
}());

» jsFiddle

同じことをインスタンスを使って行う。

  var o =  {
  flag: false,
  showAlert: function () {
        if (this.flag) {
            alert('Even Click');
            this.flag = false;
        } else {
            alert('Odd Click');
            this.flag = true;
        }
    }
};
var bar = document.getElementById('bar');
bar.onclick = o.showAlert;

外側の関数の引数も外側の関数のローカル変数でありクロージャーから見た自由変数。

function x2(value) {
    return function() {
         value = value * 2;
         return value;
    }
}
var c = x2(2);
console.log(c()); // 4
console.log(c()); // 8

関連記事

» クロージャのまとめ(2) : JavaScript

1. 実行したときのコンテキストで判断する場合を動的スコープと呼ぶ。

コメント

No comments yet.

コメントの投稿

改行と段落タグは自動で挿入されます。
メールアドレスは表示されません。