|
1 | 1 | 関数 |
2 | 2 | ================== |
3 | 3 |
|
4 | | -関数の定義、使い方などもいろいろ変わりました。表現したい機能のために、ややこしい直感的でないコードを書く必要性がかなり減っています。 |
| 4 | +関数は一連の処理に名前をつけてまとめたものです。他の人の作ったものを利用するだけでは関数などなくても、必要な処理を必要なだけ列挙すれば期待する結果が得られるコードは理論上は実現可能です。しかし、数万行の一連のコードを扱うのは事実上不可能です。 |
5 | 5 |
|
6 | | -アロー関数 |
7 | | ----------------------- |
| 6 | +そこで理解できる大きさにグループ化して、名前をつけたものが関数です。関数呼び出しはネストできるので、難しいロジックに名前をつけて関数を作り、それらのロジックを並べたちょっと複雑なタスクを人間の仕事に近い高水準な関数にできます。関数は決まった処理を単純に実行するだけではなく、引数をとって、柔軟に動作させることもできますし、返り値を返すこともできます。 |
8 | 7 |
|
9 | | -JavaScriptでは、やっかいなのが\ ``this``\ です。無名関数をコールバック関数に渡そうとすると、\ ``this``\ がわからなくなってしまう問題があります。 |
10 | | -アロー関数を使うと、その関数が定義された場所の\ ``this``\ の保持までセットで行いますので、無名関数の\ ``this``\ 由来の問題をかなり軽減できます。 |
11 | | -表記も短いため、コードの幅も短くなり、コールバックを多用するところで ``function`` という長いキーワードが頻出するのを減らすことができます。 |
| 8 | +どの程度の分量が適切かはロジックの複雑さによります。単純な仕事を延々と行っている(Reactのコンポーネントのレンダリングなど)であれば、数画面分のコードでもなんとかなるでしょうし、帰って細かく分け過ぎてしまうと、全体像の把握が難しくなります。一方で複雑なロジックだと20行でも難しいかもしれません。 |
12 | 9 |
|
13 | | -.. code-block:: ts |
14 | | - :caption: アロー関数 |
| 10 | +関数の基本形態は以下の通りです。 |
15 | 11 |
|
16 | | - // アロー関数ならその外のthisが維持される。 |
17 | | - this.button.addEventListener("click", () => { |
18 | | - this.smallAnimal.walkTo("タコ公園"); |
19 | | - }); |
| 12 | +* 関数は名前を持ちます。何かに代入したり、引数で渡す場合は省略可能です。 |
| 13 | +* 引数はカンマ区切りで名前と型を列挙していきます。引数がない場合は省略可能です。引数の型はジェネリクスを使って省略可能にもできますが、基本的に指定が必要です。 |
| 14 | +* 返り値はreturnで返します。返り値がない場合は\ ``return``\ を省略可能です。 |
| 15 | +* もしreturn文の数が一つ、または複数個あっても、毎回同じ型を返しているのであればreturnの型は省略可能です。 |
20 | 16 |
|
21 | | -アロー関数にはいくつかの記法があります。 |
22 | | -引数が1つの場合は引数のカッコを、式の結果をそのまま ``return`` する場合は式のカッコを省略できます。 |
23 | | -ただし、引数の場所に型をつけたい場合は省略するとエラーになります。 |
| 17 | +.. code-block:: |
24 | 18 |
|
25 | | -.. code-block:: ts |
26 | | - :caption: アロー関数の表記方法のバリエーション |
| 19 | + function 関数名(引数リスト): 返り値の型 { |
| 20 | + return 返り値 |
| 21 | + } |
27 | 22 |
|
28 | | - // 基本形 |
29 | | - (arg1, arg2) => { /* 式 */ }; |
| 23 | +TypeScriptの関数には次のような特徴があります。 |
30 | 24 |
|
31 | | - // 引数が1つの場合は引数のカッコを省略できる |
32 | | - // ただし型を書くとエラーになる |
33 | | - arg1 => { /* 式 */ }; |
| 25 | +* 関数そのものを変数に入れて名前をつけられるし、データ構造にも組み込める |
| 26 | +* 他の関数の引数に関数を渡せる |
| 27 | +* 関数の返り値として返すことができる |
34 | 28 |
|
35 | | - // 引数が0の場合はカッコが必要 |
36 | | - () => { /* 式 */ }; |
| 29 | +関数を受け取る関数があると、プログラムの柔軟性が飛躍的に高まります。従来のJavaScriptは関数の使い勝手が極めて便利だった一方で、言語の他の機能は少なく、関数を多用した数々のテクニックが生み出されました。一方で、かなり黒魔術な、一見すると魔法のような使われ方も多数ありました。近年ではECMAScriptやTypeScriptのバージョンアップで数多くの機能が増え、トリッキーな使い方はだいぶ減っていますが、重要なことには変わりません。 |
37 | 30 |
|
38 | | - // 式の { } を省略すると、式の結果が return される |
39 | | - arg => arg * 2; |
| 31 | +関数にはいくつかのバリエーションがあります。 |
40 | 32 |
|
41 | | - // { } をつける場合は、値を返すときは return を書かなければならない |
42 | | - arg => { |
43 | | - return arg * 2; |
44 | | - }; |
| 33 | +* 名前がない関数は無名関数、あるいはアノニマス関数と呼びます。変数に代入したり、他の関数の引数に渡す場所で利用されます。とくに、関数の内部で作られる関数を「クロージャ」と呼びます。 |
| 34 | +* 何かのオブジェクトに属する関数は「メソッド」と呼びます。 |
| 35 | +* 時間のかかる処理を行い、それが終わるまで他のタスクを途中で行える関数を非同期関数と呼びます(\ :doc:`async`\ の章で扱います) |
45 | 36 |
|
46 | | -以前は、 ``this`` がなくなってしまうため、 ``bind()`` を使って束縛したり、別の名前(ここでは ``self`` )に退避する必要がありました。 |
47 | | -そのため、\ ``var self = this;``\ と他の変数に退避するコードがバッドノウハウとして有名でした。 |
| 37 | +名前のない無名関数にはアロー演算子を使った省略記法も追加されました。ふつうの\ ``function``\ キーワードを使った宣言とほぼ同等で置き換えできることが多いのですが、動作については少し異なる部分もあります。こちらについても順をおって説明します。 |
48 | 38 |
|
49 | | -.. code-block:: ts |
50 | | - :caption: this消失を避ける古い書き方 |
| 39 | +.. code-block:: |
51 | 40 |
|
52 | | - // 旧: 無名関数のイベントハンドラではその関数が宣言されたところのthisにアクセスできない |
53 | | - var self=this; |
54 | | - this.button.addEventListener("click", function() { |
55 | | - self.smallAnimal.walkTo("タコ公園"); |
56 | | - }); |
| 41 | + const f = (引数リスト) => { |
| 42 | + return 返り値 |
| 43 | + } |
57 | 44 |
|
58 | | - // 旧: bind()で現在のthisに強制束縛 |
59 | | - this.button.addEventListener("click", (function() { |
60 | | - this.smallAnimal.walkTo("タコ公園"); |
61 | | - }).bind(this)); |
| 45 | +関数はTypeScriptの中では、プログラムの構成をつくるための重要な部品です。いたるところで使われます。言語のバージョンアップとともに、定義、使い方などもいろいろ追加されました。表現したい機能のために、ややこしい直感的でないコードを書く必要性がかなり減っています。 |
62 | 46 |
|
63 | 47 | 関数の引数と返り値の型定義 |
64 | 48 | ---------------------------------- |
@@ -292,6 +276,85 @@ setter/getterの宣言も簡単に行えるようになりました。 |
292 | 276 | } |
293 | 277 | }; |
294 | 278 |
|
| 279 | +
|
| 280 | +クロージャと\ ``this``\ とアロー関数 |
| 281 | +---------------------------------------------- |
| 282 | + |
| 283 | +関数の中で関数を定義したときに、関数は自分の定義の外にある変数を参照できます。 |
| 284 | + |
| 285 | +.. code-block:: |
| 286 | +
|
| 287 | + function a() { |
| 288 | + const b = 10; |
| 289 | + function c() { |
| 290 | + console.log({b}); // bが表示される |
| 291 | + } |
| 292 | + c(); |
| 293 | + } |
| 294 | +
|
| 295 | +実行時の親子関係ではなく、ソースコードという定義時の親子関係を元にしてスコープが決定されます。これをレキシカルスコープと呼びます。また、このように自分が定義された場所の外の変数を束縛した関数を「クロージャ」と呼びます。TypeScriptでは関数を駆使してロジックを組み上げていきますので、この機能はとても重要です。 |
| 296 | + |
| 297 | +以前はJavaのようなオブジェクトを実装するために、関数内部の変数をプライベートメンバー変数のように扱うテクニックがかつてありました。クラスの機能が公式のサポートされたので、今では重要度は低くなっているし、そもそも隠す必要性もあまりなので使うことはありませんが、頭の体操にはなるので、興味がある方は調べてみてください。 |
| 298 | + |
| 299 | +レキシカルスコープは今では多くの言語が持っている機能なので、わざわざ名前を呼ぶこともありませんが、TypeScriptでは、知らないと落とし穴に落ちる可能性のあるやや重要な機能となります。前項でオブジェクトの中の関数定義を紹介しました。ここでは、予め定義された変数のように\ ``this``\ を使っています。しかしこれは変数ではなく、特別な識別子です。レキシカルスコープで束縛できません。クロージャかつ、\ ``this``\ への束縛ができる新文法としてアロー関数が追加されました。 |
| 300 | + |
| 301 | +アロー関数 |
| 302 | +~~~~~~~~~~~~~~~~~~~~~~~ |
| 303 | + |
| 304 | +JavaScriptでは、やっかいなのが\ ``this``\ です。無名関数をコールバック関数に渡そうとすると、\ ``this``\ がわからなくなってしまう問題があります。 |
| 305 | +アロー関数を使うと、その関数が定義された場所の\ ``this``\ の保持までセットで行いますので、無名関数の\ ``this``\ 由来の問題をかなり軽減できます。 |
| 306 | +表記も短いため、コードの幅も短くなり、コールバックを多用するところで ``function`` という長いキーワードが頻出するのを減らすことができます。 |
| 307 | + |
| 308 | +.. code-block:: ts |
| 309 | + :caption: アロー関数 |
| 310 | +
|
| 311 | + // アロー関数ならその外のthisが維持される。 |
| 312 | + this.button.addEventListener("click", () => { |
| 313 | + this.smallAnimal.walkTo("タコ公園"); |
| 314 | + }); |
| 315 | +
|
| 316 | +アロー関数にはいくつかの記法があります。 |
| 317 | +引数が1つの場合は引数のカッコを、式の結果をそのまま ``return`` する場合は式のカッコを省略できます。 |
| 318 | +ただし、引数の場所に型をつけたい場合は省略するとエラーになります。 |
| 319 | + |
| 320 | +.. code-block:: ts |
| 321 | + :caption: アロー関数の表記方法のバリエーション |
| 322 | +
|
| 323 | + // 基本形 |
| 324 | + (arg1, arg2) => { /* 式 */ }; |
| 325 | +
|
| 326 | + // 引数が1つの場合は引数のカッコを省略できる |
| 327 | + // ただし型を書くとエラーになる |
| 328 | + arg1 => { /* 式 */ }; |
| 329 | +
|
| 330 | + // 引数が0の場合はカッコが必要 |
| 331 | + () => { /* 式 */ }; |
| 332 | +
|
| 333 | + // 式の { } を省略すると、式の結果が return される |
| 334 | + arg => arg * 2; |
| 335 | +
|
| 336 | + // { } をつける場合は、値を返すときは return を書かなければならない |
| 337 | + arg => { |
| 338 | + return arg * 2; |
| 339 | + }; |
| 340 | +
|
| 341 | +以前は、 ``this`` がなくなってしまうため、 ``bind()`` を使って束縛したり、別の名前(ここでは ``self`` )に退避する必要がありました。 |
| 342 | +そのため、\ ``var self = this;``\ と他の変数に退避するコードがバッドノウハウとして有名でした。 |
| 343 | + |
| 344 | +.. code-block:: ts |
| 345 | + :caption: this消失を避ける古い書き方 |
| 346 | +
|
| 347 | + // 旧: 無名関数のイベントハンドラではその関数が宣言されたところのthisにアクセスできない |
| 348 | + var self=this; |
| 349 | + this.button.addEventListener("click", function() { |
| 350 | + self.smallAnimal.walkTo("タコ公園"); |
| 351 | + }); |
| 352 | +
|
| 353 | + // 旧: bind()で現在のthisに強制束縛 |
| 354 | + this.button.addEventListener("click", (function() { |
| 355 | + this.smallAnimal.walkTo("タコ公園"); |
| 356 | + }).bind(this)); |
| 357 | +
|
295 | 358 | ``this``\ を操作するコードは書かない(1) |
296 | 359 | -------------------------------------------------- |
297 | 360 |
|
@@ -370,11 +433,11 @@ setter/getterの宣言も簡単に行えるようになりました。 |
370 | 433 |
|
371 | 434 | 関数についてさまざまなことを紹介してきました。 |
372 | 435 |
|
373 | | -* アロー関数 |
374 | 436 | * 関数の引数と返り値の型定義 |
375 | 437 | * 関数を扱う変数の型定義 |
376 | 438 | * デフォルト引数 |
377 | 439 | * 関数を含むオブジェクトの定義方法 |
| 440 | +* クロージャと\ ``this``\ とアロー関数 |
378 | 441 | * thisを操作するコードは書かない(1) |
379 | 442 | * 即時実行関数はもう使わない |
380 | 443 |
|
|
0 commit comments