回答編集履歴
4
みえるのはすべて
answer
CHANGED
@@ -33,7 +33,7 @@
|
|
33
33
|
// <=== 大域スコープ
|
34
34
|
```
|
35
35
|
|
36
|
-
JavaScriptでは主に大域スコープ(global scope)、関数スコープ(function scope)、ブロックスコープ(block scope)の三つがあります(他にevalスコープ(eval scope)とモジュールスコープ(module scope)がありますが、ここでは省略します。なお、名称はECMAScript 2016仕様書での○○DeclarationInstantiationとなっているものから取ってきています)。変数は宣言された場所のスコープに関連づけられ、そのスコープの範囲内で有効になります(`var`で宣言された場合のみ特殊で、ブロックスコープ上であっても一番間近の外側にある大域スコープまたは関数スコープに関連づけられます)。上では、`x`は大域スコープ、`y`は関数スコープ、`z`はブロックスコープが有効範囲です。しかし、スコープは外から内へ浸食しており、内側のスコープは外側のスコープを見ることができます。ですので、`x`
|
36
|
+
JavaScriptでは主に大域スコープ(global scope)、関数スコープ(function scope)、ブロックスコープ(block scope)の三つがあります(他にevalスコープ(eval scope)とモジュールスコープ(module scope)がありますが、ここでは省略します。なお、名称はECMAScript 2016仕様書での○○DeclarationInstantiationとなっているものから取ってきています)。変数は宣言された場所のスコープに関連づけられ、そのスコープの範囲内で有効になります(`var`で宣言された場合のみ特殊で、ブロックスコープ上であっても一番間近の外側にある大域スコープまたは関数スコープに関連づけられます)。上では、`x`は大域スコープ、`y`は関数スコープ、`z`はブロックスコープが有効範囲です。しかし、スコープは外から内へ浸食しており、内側のスコープは外側のスコープを見ることができます。ですので、`x`や`y`も`z`と同じように一番内側のブロックスコープでもアクセスできます。同時に、並列にあるスコープはそれぞれ独立しています。ですので、関数`g`内の`y`は関数`f`内の`y`への代入に影響を受けません。
|
37
37
|
|
38
38
|
※ スコープの種類や名称は言語によって異なります。
|
39
39
|
※ これはJavaScriptの場合であって、他の言語でも同じとは限りません。例えばRubyは、メソッドスコープから一番外側のトップスコープにあるローカル変数は見えません。
|
3
`var`について注意事項を追加
answer
CHANGED
@@ -33,7 +33,7 @@
|
|
33
33
|
// <=== 大域スコープ
|
34
34
|
```
|
35
35
|
|
36
|
-
JavaScriptでは主に大域スコープ(global scope)、関数スコープ(function scope)、ブロックスコープ(block scope)の三つがあります(他にevalスコープ(eval scope)とモジュールスコープ(module scope)がありますが、ここでは省略します。なお、名称はECMAScript 2016仕様書での○○DeclarationInstantiationとなっているものから取ってきています)。変数は宣言された場所のスコープに関連づけられ、そのスコープの範囲内で有効になります。上では、`x`は大域スコープ、`y`は関数スコープ、`z`はブロックスコープが有効範囲です。しかし、スコープは外から内へ浸食しており、内側のスコープは外側のスコープを見ることができます。ですので、`x`は一番内側のブロックスコープでもアクセスできます。同時に、並列にあるスコープはそれぞれ独立しています。ですので、関数`g`内の`y`は関数`f`内の`y`への代入に影響を受けません。
|
36
|
+
JavaScriptでは主に大域スコープ(global scope)、関数スコープ(function scope)、ブロックスコープ(block scope)の三つがあります(他にevalスコープ(eval scope)とモジュールスコープ(module scope)がありますが、ここでは省略します。なお、名称はECMAScript 2016仕様書での○○DeclarationInstantiationとなっているものから取ってきています)。変数は宣言された場所のスコープに関連づけられ、そのスコープの範囲内で有効になります(`var`で宣言された場合のみ特殊で、ブロックスコープ上であっても一番間近の外側にある大域スコープまたは関数スコープに関連づけられます)。上では、`x`は大域スコープ、`y`は関数スコープ、`z`はブロックスコープが有効範囲です。しかし、スコープは外から内へ浸食しており、内側のスコープは外側のスコープを見ることができます。ですので、`x`は一番内側のブロックスコープでもアクセスできます。同時に、並列にあるスコープはそれぞれ独立しています。ですので、関数`g`内の`y`は関数`f`内の`y`への代入に影響を受けません。
|
37
37
|
|
38
38
|
※ スコープの種類や名称は言語によって異なります。
|
39
39
|
※ これはJavaScriptの場合であって、他の言語でも同じとは限りません。例えばRubyは、メソッドスコープから一番外側のトップスコープにあるローカル変数は見えません。
|
2
ECMAScript 2017はまだでてないよ!
answer
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
※ Emacs Lisp等、動的スコープ(dynamic scope)を採用している言語もありますが、少数です。
|
7
7
|
|
8
|
-
クロージャを語るつもりが、環境やら静的スコープとか出てきました。まず、スコープとは何かというと変数が見える範囲(視野)の事です。下記例を見てみましょう。(以下は静的スコープであるECMAScript
|
8
|
+
クロージャを語るつもりが、環境やら静的スコープとか出てきました。まず、スコープとは何かというと変数が見える範囲(視野)の事です。下記例を見てみましょう。(以下は静的スコープであるJavaScript(ECMAScript 2016)での例になりますので、動的スコープでは全く異なることに注意してください)
|
9
9
|
|
10
10
|
```JavaScript
|
11
11
|
// 大域スコープ ===>
|
1
クロージャではない関数などない
answer
CHANGED
@@ -56,7 +56,7 @@
|
|
56
56
|
|
57
57
|
大域スコープの`x`とブロックスコープの`x`は別の物であるため、大域スコープの`x`は変更されません。
|
58
58
|
|
59
|
-
このように、各スコープでわかれたそれぞれの場所を**環境**と言います。それぞれの環境にはそのスコープで宣言された変数が**束縛**されており、また、外側にどんな環境があるかも知っています。
|
59
|
+
このように、各スコープでわかれたそれぞれの場所を**環境**と言います。それぞれの環境にはそのスコープで宣言された変数が**束縛**されており、また、外側にどんな環境があるかも知っています(外側の環境をどんどん繋いでいく仕組みをスコープチェーンといいます)。
|
60
60
|
|
61
61
|
ここでもう一つ重要な概念を話さなければなりません。それは環境の寿命、つまり、そのスコープにある変数の寿命です。スコープは内側に侵食していますが、外側にはみ出すことはできません。つまり、スコープ内の処理を全て終えれば、そのスコープからしか見えない変数はアクセスができなくなる=破棄してもよいということです。一番最初の例を見てください。ブロックスコープを処理するときは、関数`f`スコープも大域スコープも終わっていることはありません。ですが、ブロックが終われば`z`は用済みですし、関数`f`も終われば、関数`f`だけ有効な`y`も用済みです。アクセス不可能な変数をいつまでも保持するのはメモリの無駄ですので、そのスコープの環境が破棄され、全ての変数はいずれGCに回収されます。
|
62
62
|
|
@@ -92,8 +92,13 @@
|
|
92
92
|
|
93
93
|
ということで、最初のひとことで言っていた「環境の包み込み」の所に戻るというわけです。そして、包み込んだ**環境**を一緒に持ち出せる所がクロージャの最も重要な所と言っても過言ではありません。
|
94
94
|
|
95
|
+
###JavaScriptでクロージャである関数とクロージャではない関数
|
96
|
+
結論から言いますと、JavaScriptでの全ての関数(名前有り無しなどに関わらず)はクロージャを**必ず含んでいます**。理由は二つあります。一つは、JavaScriptは関数定義時にその中身を一切評価しないため、中で外側の環境にある変数を使っているかわからないと言うこと。もう一つは、その関数が現在のスコープが終了した後にも使われる事になるかを定義時に判断できないと言うこと。この二つの理由により、JavaScriptエンジンはその関数がクロージャとして動く必要があるのか、それともクロージャでなくても良いのかを関数が作成される定義時に判断することができないからです。
|
97
|
+
|
98
|
+
※ 他の言語も同じとは限りません。他の方が示したようにPHPでは普通の関数をクロージャにするかしないかをuseキーワードで選択できます(ただし無名関数は必ずクロージャになる、というよりClosureのインスタンスを作るのが無名関数であるらしい)。
|
99
|
+
|
95
100
|
###JavaScript以外のクロージャ
|
96
|
-
クロージャも実は色々種類が
|
101
|
+
クロージャも実は色々種類があり、実装も様々です。上のことは他の言語でもだいたい同じように言えますが、細部が異なります。
|
97
102
|
|
98
103
|
* Rubyは無名関数ではなくブロック(do ~ end)がクロージャを作ります。クロージャの和訳が「関数閉包」となっていますが、何かしらの処理を定義された場所の環境と一緒に持ち出すことができるのであれば、**関数であるかどうかは関係ありません**。
|
99
104
|
|
@@ -142,4 +147,6 @@
|
|
142
147
|
}
|
143
148
|
```
|
144
149
|
|
145
|
-
* Haskellは遅延評価という仕組みであるため、束縛している変数が実際に評価されるときにはスコープが終了した後になる場合があります。そのため、そのときの各変数のコピーでは無く、環境自体を含んでいる必要があります。つまり、**Haskellは本当のクロージャを実装しています**。ただし、immutableという性質上、JavaScriptのカウンタの例のようなことはできません。
|
150
|
+
* Haskellは遅延評価という仕組みであるため、束縛している変数が実際に評価されるときにはスコープが終了した後になる場合があります。そのため、そのときの各変数のコピーでは無く、環境自体を含んでいる必要があります。つまり、**Haskellは本当のクロージャを実装しています**。ただし、immutableという性質上、JavaScriptのカウンタの例のようなことはできません。
|
151
|
+
* Pythonはちょっと特殊っぽいです。調査中。
|
152
|
+
* Schemeはクロージャの元祖らしいです。
|