回答:どれが「クロージャ」でしょうか?

問題:どれが「クロージャ」でしょうか? - hogehoge の回答

期待させて申し訳ないですが、はっきりとした解等を定めていません。ということで解答ではなく、回答で。

クロージャの定義を以下の様に定めました。

引数以外の変数を実行時の環境ではなく、自身が定義された環境(静的スコープ)において解決することを特徴とする。

クロージャ - Wikipedia

JavaScriptの関数オブジェクトは定義時にスコープを定めてチェーンを形成します。このチェーンが変化することはありません。上記定義の言葉通りに考えるならば、答えは6, 7, 9以外の全てになると思います。

ただし、よくある説明では、関数の中で定義される関数オブジェクトのみを「クロージャ」と呼んでいる様に思えます。

var scope = "global";

function func1 () {
  return scope;
}

function foo () {
  var scope = "local";

  return func1();
}

foo(); // "global"

func1 が foo から実行されるわけですが、func1 は引数以外の変数を実行時の環境ではなく、自身が定義された環境から変数scopeを割り出します。func1 が定義されたのはグローバルなスコープです。例え foo 関数の中で実行されようとも foo 関数内の変数がスコープに入ることはありません。

func1 は関数の中で定義されているわけではありません。定義にある様な特徴は持っているのに「クロージャ」ではないのでしょうか? ボクには分かりません。

例え、外部にグローバルのスコープしか持たない関数であっても、きちんと自身が定義された環境を持っています。あえて「クロージャ」と呼ばれることはないかもしれません。しかし、その特徴は活かされないかもしれませんが、クロージャであることに変わりないと思うのです。

ただ、少し嫌な関数オブジェクトの定義方法があります。Functionコンストラクタと間接的なeval(indirect call)です。

Functionコンストラク

Functionコンストラクタは new Function(...) みたいなやつのことです。因みに、new Function(...)Function(...)に違いはありません、単なる引っかけとして2種類用意してみただけです。

コンストラクタの挙動によると、最後に以下の様に書かれています。

11. Return a new Function object created as specified in 13.2 passing P as the FormalParameterListopt and body as the FunctionBody. Pass in the Global Environment as the Scope parameter and strict as the Strict flag.

注目すべきは、ScopeをGlobal Environmentとしていることです。Global Environmentというのは要するにグローバルスコープのことです。ここ、普通のFunctionStatementなどでは、実行中の環境をScopeに定めているのですが、Functionコンストラクタではグローバルスコープ決め打ちです。

さて、定義時にこのスコープは定められ、しかも変化しません。しかし、そもそもの定義するスコープが自身が定義された環境ではなくなっています。

よって、これはクロージャとは呼べないのではないでしょうか。(グローバルで定義されるFunctionコンストラクタによる関数作成はクロージャと呼べる...のか?)

間接的なeval

  • eval('(function(){ })')
  • var e = eval; e('(function(){ })')

問題で上記2つを用意しましたが、挙動が変わります。

前者の場合は、evalされた時のスコープ環境を引き継ぎますが、後者はグローバルに設定されます。そのスコープがFunctionExpressionに渡ることになります。

前者は良いのですが、後者は Functionコンストラクタのときと同じで自身が定義された環境と言えるのか怪しい感じになります。

ただ、スコープを歪めたのはevalであり、実行コードである関数式はそれを正確に引き継いだだけだとも言えます。難しいです。

世間での「クロージャ

以上、Wikipediaに定義からアレコレ考えて書いてみました。

ボクはこの定義のように静的スコープを持つ関数をクロージャだというものしか知りません。(はてなキーワードにも同じようなことが書かれています)

関数の中に関数を書ける、というのは説明するのに都合が良いというだけで、クロージャの本質ではないと思うわけですが、 この考えだとどうしても世間とずれが生じているように思えます。 はっきりとした答えが出せないのです。皆さんはどうお考えでしょうか? 何らかの反応があると嬉しいです。