回答編集履歴

1

コメントを受けて追記しました。

2025/01/11 03:53

投稿

TakaiY
TakaiY

スコア13907

test CHANGED
@@ -12,7 +12,7 @@
12
12
  と書いたとき、2行目の右辺の`a`は、代入されているオブジェクトである1に置き換わり、bには1が代入されます。
13
13
  3行目のreturnの対象である`a`も同様に代入されている1に置き換わり、1が返ります。
14
14
 
15
- 変数に代入されるブジェクトにはいろいろなものがありますが、この質問で重要なのは、**関数そのものも変数に代入することができる**点です。また、関数定義で名前として定義したものも**変数**として扱うことができます。以下の定義のとき`a`は変数であり、その変数には定義した関数が代入されています。
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がりませんので返り値が言語の仕様で`undefined`という値とされています。
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
+