Skip to content

Commit a65f1b6

Browse files
committed
js5まで
1 parent 144670d commit a65f1b6

File tree

3 files changed

+476
-0
lines changed

3 files changed

+476
-0
lines changed

app/pagesList.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ export const pagesList = [
4747
{id: 1, title: "JavaScriptへようこそ"},
4848
{id: 2, title: "基本構文とデータ型"},
4949
{id: 3, title: "制御構文"},
50+
{id: 4, title: "関数とクロージャ"},
51+
{id: 5, title: "'this'の正体"},
5052
]
5153
},
5254
{

public/docs/javascript-4.md

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
# 第4章: 関数とクロージャ
2+
3+
JavaScriptにおいて関数はオブジェクトの一種です。つまり、変数に代入したり、他の関数の引数として渡したり、関数から戻り値として返したりすることができます。この柔軟性が、JavaScriptの設計パターンの核心を担っています。
4+
5+
## 関数の定義(関数宣言 vs 関数式)
6+
7+
JavaScriptには関数を定義する方法が主に2つあります。「関数宣言」と「関数式」です。これらは似ていますが、**巻き上げ(Hoisting)** の挙動が異なります。
8+
9+
### 1\. 関数宣言 (Function Declaration)
10+
11+
古くからある定義方法です。スクリプトの実行前に読み込まれるため、定義する前の行から呼び出すことができます。
12+
13+
```js:function_declaration.js
14+
console.log(greet("Alice")); // 定義前でも呼び出せる
15+
16+
function greet(name) {
17+
return `Hello, ${name}!`;
18+
}
19+
```
20+
21+
```js-exec:function_declaration.js
22+
Hello, Alice!
23+
```
24+
25+
### 2\. 関数式 (Function Expression)
26+
27+
変数に関数を代入するスタイルです。変数の代入は実行時に行われるため、定義する前に呼び出すとエラーになります。現代のJavaScript開発では、意図しない巻き上げを防ぐためにこちら(または後述のアロー関数)が好まれる傾向にあります。
28+
29+
```js:function_expression.js
30+
// 定義前に呼び出すと... ReferenceError: Cannot access 'sayHi' before initialization
31+
// console.log(sayHi("Bob"));
32+
33+
const sayHi = function(name) {
34+
return `Hi, ${name}!`;
35+
};
36+
37+
console.log(sayHi("Bob"));
38+
```
39+
40+
```js-exec:function_expression.js
41+
Hi, Bob!
42+
```
43+
44+
## アロー関数 (=\>) の構文と特徴
45+
46+
ES2015 (ES6) で導入されたアロー関数は、関数式をより短く記述するための構文です。Javaのラムダ式やPythonのlambdaに似ていますが、いくつか独自の特徴があります。
47+
48+
### 基本構文
49+
50+
`function` キーワードを省略し、`=>` (矢印) を使って定義します。
51+
52+
```js:arrow_function.js
53+
// 従来の関数式
54+
const add = function(a, b) {
55+
return a + b;
56+
};
57+
58+
// アロー関数
59+
const addArrow = (a, b) => {
60+
return a + b;
61+
};
62+
63+
console.log(addArrow(3, 5));
64+
```
65+
66+
```js-exec:arrow_function.js
67+
8
68+
```
69+
70+
### 省略記法
71+
72+
アロー関数には強力な省略記法があります。
73+
74+
1. **引数が1つの場合**: カッコ `()` を省略可能。
75+
2. **処理が1行でreturnする場合**: 中括弧 `{}``return` キーワードを省略可能(暗黙のreturn)。
76+
77+
```js-repl:4
78+
> const square = x => x * x; // 引数の()とreturnを省略
79+
> square(5);
80+
25
81+
82+
> const getUser = (id, name) => ({ id: id, name: name }); // オブジェクトを返す場合は()で囲む
83+
> getUser(1, "Gemini");
84+
{ id: 1, name: 'Gemini' }
85+
```
86+
87+
> **注意:** アロー関数は単なる短縮記法ではありません。「`this` を持たない」という重要な特徴がありますが、これについては**第5章**で詳しく解説します。
88+
89+
## 引数:デフォルト引数、Restパラメータ (...)
90+
91+
関数の柔軟性を高めるための引数の機能を見ていきましょう。
92+
93+
### デフォルト引数
94+
95+
引数が渡されなかった場合(または `undefined` の場合)に使用される初期値を設定できます。
96+
97+
```js:default_args.js
98+
const connect = (host = 'localhost', port = 8080) => {
99+
console.log(`Connecting to ${host}:${port}...`);
100+
};
101+
102+
connect(); // 両方省略
103+
connect('127.0.0.1'); // portはデフォルト値
104+
connect('example.com', 22); // 両方指定
105+
```
106+
107+
```js-exec:default_args.js
108+
Connecting to localhost:8080...
109+
Connecting to 127.0.0.1:8080...
110+
Connecting to example.com:22...
111+
```
112+
113+
### Restパラメータ (残余引数)
114+
115+
引数の数が不定の場合、`...` を使うことで、残りの引数を**配列として**受け取ることができます。以前は `arguments` オブジェクトを使っていましたが、Restパラメータの方が配列メソッド(`map`, `reduce`など)を直接使えるため便利です。
116+
117+
```js:rest_params.js
118+
const sum = (...numbers) => {
119+
// numbersは本物の配列 [1, 2, 3, 4, 5]
120+
return numbers.reduce((acc, curr) => acc + curr, 0);
121+
};
122+
123+
console.log(sum(1, 2, 3));
124+
console.log(sum(10, 20, 30, 40, 50));
125+
```
126+
127+
```js-exec:rest_params.js
128+
6
129+
150
130+
```
131+
132+
## スコープチェーンとレキシカルスコープ
133+
134+
JavaScriptの変数の有効範囲(スコープ)を理解するために、「レキシカルスコープ」という概念を知る必要があります。
135+
136+
* **レキシカルスコープ (Lexical Scope):** 関数が「どこで呼び出されたか」ではなく、**「どこで定義されたか」**によってスコープが決まるというルールです。
137+
* **スコープチェーン (Scope Chain):** 変数を探す際、現在のスコープになければ、定義時の外側のスコープへと順番に探しに行く仕組みです。
138+
139+
```js:scope.js
140+
const globalVar = "Global";
141+
142+
function outer() {
143+
const outerVar = "Outer";
144+
function inner() {
145+
const innerVar = "Inner";
146+
// innerの中からouterVarとglobalVarが見える(スコープチェーン)
147+
return `${globalVar} > ${outerVar} > ${innerVar}`;
148+
}
149+
return inner();
150+
}
151+
152+
console.log(outer());
153+
```
154+
155+
```js-exec:scope.js
156+
Global > Outer > Inner
157+
```
158+
159+
## クロージャ:関数が状態を持つ仕組み
160+
161+
クロージャ (Closure) は、この章の最重要トピックです。
162+
一言で言えば、**「外側の関数のスコープにある変数を、外側の関数の実行終了後も参照し続ける関数」**のことです。
163+
164+
通常、関数(`createCounter`)の実行が終わると、そのローカル変数(`count`)はメモリから破棄されます。しかし、その変数を参照している内部関数(`increment`)が存在し、その内部関数が外部に返された場合、変数は破棄されずに保持され続けます。
165+
166+
### クロージャの実例:カウンタ
167+
168+
プライベートな変数を持つカウンタを作ってみましょう。
169+
170+
```js:closure_counter.js
171+
const createCounter = () => {
172+
let count = 0; // この変数は外部から直接アクセスできない(プライベート変数的な役割)
173+
174+
return () => {
175+
count++;
176+
console.log(`Current count: ${count}`);
177+
};
178+
};
179+
180+
const counterA = createCounter(); // counterA専用のスコープ(環境)が作られる
181+
const counterB = createCounter(); // counterB専用のスコープが別に作られる
182+
183+
counterA(); // 1
184+
counterA(); // 2
185+
counterA(); // 3
186+
187+
console.log("--- switching to B ---");
188+
189+
counterB(); // 1 (Aの状態とは独立している)
190+
```
191+
192+
```js-exec:closure_counter.js
193+
Current count: 1
194+
Current count: 2
195+
Current count: 3
196+
--- switching to B ---
197+
Current count: 1
198+
```
199+
200+
### なぜクロージャを使うのか?
201+
202+
1. **カプセル化 (Encapsulation):** 変数を隠蔽し、特定の関数経由でしか変更できないようにすることで、予期せぬバグを防ぎます。
203+
2. **状態の保持:** グローバル変数を使わずに、関数単位で永続的な状態を持てます。
204+
3. **関数ファクトリ:** 設定の異なる関数を動的に生成する場合に役立ちます。
205+
206+
## この章のまとめ
207+
208+
* **関数定義:** 巻き上げが起こる「関数宣言」と、起こらない「関数式(アロー関数含む)」がある。
209+
* **アロー関数:** `(args) => body` の形式で記述し、`this` の挙動が従来と異なる。
210+
* **引数:** デフォルト引数とRestパラメータ(`...args`)で柔軟な引数処理が可能。
211+
* **レキシカルスコープ:** 関数は「定義された場所」のスコープを記憶する。
212+
* **クロージャ:** 内部関数が外部関数の変数を参照し続ける仕組み。データの隠蔽や状態保持に使われる。
213+
214+
## 練習問題1: アロー関数への書き換え
215+
216+
以下の関数宣言を、アロー関数 `isEven` に書き換えてください。ただし、省略可能な記号(カッコやreturnなど)は可能な限り省略して最短で記述してください。
217+
218+
```js:practice4_1.js
219+
function isEven(n) {
220+
return n % 2 === 0;
221+
}
222+
```
223+
224+
```js-exec:practice4_1.js
225+
```
226+
227+
### 問題2: クロージャによる掛け算生成器
228+
229+
`createMultiplier` という関数を作成してください。この関数は数値 `x` を引数に取り、呼び出すたびに「引数を `x` 倍して返す関数」を返します。
230+
231+
**使用例:**
232+
233+
```js:practice4_2.js
234+
// ここに関数を作成
235+
236+
237+
const double = createMultiplier(2);
238+
console.log(double(5)); // 10
239+
240+
const triple = createMultiplier(3);
241+
console.log(triple(5)); // 15
242+
```
243+
244+
```js-exec:practice4_2.js
245+
```

0 commit comments

Comments
 (0)