回答編集履歴
1
コメントを受けて追記しました。
test
CHANGED
@@ -12,7 +12,7 @@
|
|
12
12
|
と書いたとき、2行目の右辺の`a`は、代入されているオブジェクトである1に置き換わり、bには1が代入されます。
|
13
13
|
3行目のreturnの対象である`a`も同様に代入されている1に置き換わり、1が返ります。
|
14
14
|
|
15
|
-
変数に代入される
|
15
|
+
変数に代入されるオブジェクトにはいろいろなものがありますが、この質問で重要なのは、**関数そのものも変数に代入することができる**点です。また、関数定義で名前として定義したものも**変数**として扱うことができます。以下の定義のとき`a`は変数であり、その変数には定義した関数が代入されています。
|
16
16
|
```javascript
|
17
17
|
function a() {
|
18
18
|
num = num + 1;
|
@@ -28,7 +28,7 @@
|
|
28
28
|
2行目では、`a`に代入されている関数がreturnされることになります。
|
29
29
|
|
30
30
|
さて、`a`に関数が代入されているとき、その関数を呼び出す(実行する)にはどのようにすればいいかというと、aの後に`()`を付けて、中に必要な引数を入れてやればいいのです。
|
31
|
-
ようするに、`a()`という記述はaに代入されている関数を呼び出す(実行する)ことを表わしています。また、そのとき、`a()`の部分は、aに代入されている関数を実行したときの
|
31
|
+
ようするに、`a()`という記述はaに代入されている関数を呼び出す(実行する)ことを表わしています。また、そのとき、`a()`の部分は、aに代入されている関数を実行したときの返り値になります。
|
32
32
|
|
33
33
|
|
34
34
|
これらを踏まえて質問のコードを見てみましょう。
|
@@ -57,7 +57,34 @@
|
|
57
57
|
このようになっています。
|
58
58
|
1行目で、incrementFactoryが実行され、返り値がfnに代入されます。 incrementFactoryの返り値は定義した関数aですから、変数fnにはその価数そのものが入ります。 後の行fn()として実行されていますが、fnに入っているのはaとして定義された関数ですのでそのとおり動作します。
|
59
59
|
|
60
|
-
ここでincrementFactoryを書き換えて、`return a()`とするとどうなるでしょう。この関数は内部で定義している関数aを**実行してその結果を返す**関数になってしまいます。関数aにはreturnが
|
60
|
+
ここでincrementFactoryを書き換えて、`return a()`とするとどうなるでしょう。この関数は内部で定義している関数aを**実行してその結果を返す**関数になってしまいます。関数aにはreturnがありませんので返り値が言語の仕様で`undefined`という値とされています。
|
61
61
|
なのでそのように書き換えると、1行目でfnにはundefinedという値が入ります。そして、以降の処理でそれに()を付けて関数として実行しようとしたときに、undefinedは関数でなないので、「Uncaught TypeError: fn is not a function」(fnは関数ではありません)というエラーになるのです。
|
62
62
|
|
63
|
+
---
|
63
64
|
|
65
|
+
> なぜJavaScript1では、その「状態を保持する」のルールは適用されないのでしょうか
|
66
|
+
|
67
|
+
クロージャというのは乱暴に言えば「環境が付きの関数」で、そのものがオブジェクトであって、**関数定義時に作られます**。 よく、コードそのものがクロージャだと捉えている人がいますが、そうではなく、数値や配列などのように、**そのコードを元に作られるオブジェクト**です。
|
68
|
+
質問のコードでは、関数incrementFactory(これもクロージャですが)の中で関数aが定義されています。関数aが定義が実行されると、関数aのオブジェクトは値が0の変数numが含んだクロージャになります。ここで重要なのは、**関数aが関数incrementFactoryの中で定義されているため、incrementFactoryが呼ばれる度に新しい関数aが生成される**ということです。関数のオブジェクトの概念を()を使って表わすと、関数aは生成されたときに(関数、num=0)のようなものになり、incrementFactoryが呼ばれるたびにこれが1つ生成されるということになります。
|
69
|
+
|
70
|
+
これを踏まえて、JavaScript1を見ると、以下のようになっています。
|
71
|
+
```javascript
|
72
|
+
incrementFactory();
|
73
|
+
incrementFactory();
|
74
|
+
incrementFactory();
|
75
|
+
incrementFactory();
|
76
|
+
```
|
77
|
+
JavaScript1の定義では、関数incrementFactoryは呼ばれると、(関数、num=0)が生成され、returnのタイミングで関数aが実行されます。 実行時のnumは0ですが1が足されて結果の1が表示されます。 incrementFactoryは4回呼ばれますから、(関数、num=0)が4回生成され、その度に関数aが呼ばれるので、1が4つ出力されます。
|
78
|
+
|
79
|
+
JavaScirpt2ではどうでしょう。
|
80
|
+
```javascirpt
|
81
|
+
const fn = incrementFactory();
|
82
|
+
|
83
|
+
fn();
|
84
|
+
fn();
|
85
|
+
fn();
|
86
|
+
fn();`
|
87
|
+
```
|
88
|
+
JavaScript2の定義では、関数incrementFactoryは呼ばれると、(関数、num=0)が生成され、return でそれが返されます。1行目でincrementFactoryが呼ばれて生成されて返された(関数、num=0)はfnに代入されます。
|
89
|
+
最初にfnが呼ばれて関数が実行されると、0だったnumに1が足され、結果の1が表示されます。 実行後のクロージャは(関数、num=1)になっています。次にfnが呼ばれると、**fnは新しく定義されたりしていないので**(関数、num=1)が実行され、2が表示され、結果としてクロージャは((関数、num=2)になります。
|
90
|
+
|