Part9:関数

海未「今回は関数についてです。はいそこ、嫌な顔をしない」
穂乃果「うえぇ・・・」
海未「JavaScriptは関数型言語であるという解釈もあるようですから、関数はとても重要な概念になります。しっかり理解してくださいね」


関数定義

海未「関数というのは、概念論に走ると色々難解な定義が出てくるのですが、ここでは処理の集まりに名前を付けたものと理解すればいいでしょう」
ことり「数学的には、y = f(x)みたいにxを与えるとyが得られるやつだよね?」
海未「ことりは話が早いですね。それは関数fにxという引数を与えてyという戻り値を得ている、ということです。JavaScriptの関数呼び出しも同じですね」
穂乃果「むむむ・・・」

海未「ひとまず例を見てみましょうか」

ほのこと
ほのうみ

海未「functionでpair関数を宣言しています。これはmという引数を1つ取ります。関数内では”ほの”に引数をつなげて戻り値としています」
ことり「mに何を渡すかで結果が変わるんだよね」
海未「はい。それが関数の特徴です。呼び出し側で渡している”こと”や”うみ”を実引数、関数側のmを仮引数と呼びます」
ことり「穂乃果ちゃん、ついてこれてる?」
穂乃果「このコード見れば、一応わかるよ・・・」

海未「引数に関連して、ECMAScript6ではデフォルト仮引数という仕組みがあります。これは仮引数のデフォルト値を決めておけるものです」

ほのundefined
ほのこと

海未「引数が渡されなかった場合はundefinedになりますが、このpair2関数のようにm = "こと"と書いておくと、undefinedのかわりに"こと"が使われます」
穂乃果「引数がたくさんあるとき楽ができそう!」
海未「この穂乃果の思考回路をうまく活用する方法はないものでしょうか・・・」

海未「先ほどの例では関数に名前を付けて定義しました。それ以外にも関数の定義方法があります。関数式を見てみましょう」

海未「これはさきほどのコードを関数式で書き直したものです。pairという変数に関数を代入していますが、関数自体に名前はありません。これを無名関数といいます」
ことり「pairって変数には関数が入ってるってこと?」
海未「はい。ですから、pair()をつけると代入された関数を呼び出すことができます」
穂乃果「これは何が便利なのかな?」
海未「JavaScriptでは高階関数、つまり関数を受け取る関数を扱うことができます。その場合に使い捨ての関数が定義できると便利なのです」

海未「ECMAScript6ではアロー関数というものが導入されました」

海未「先ほどのコードと同じ内容ですが、短く書けますね」
穂乃果「functionとか書かなくていいんだ」
海未「アロー関数にはもうひとつ、thisの扱いという特徴があるのですが・・・まだ、出てきていない内容ですね。オブジェクトのところで説明します」


スコープ

海未「関数を導入すると、変数のスコープの問題が出てきます。関数は入れ子にできますが、その場合のスコープは」

チーズケーキ
13:47:05.582 ReferenceError: umiFav is not defined
kotori@Scratchpad/1:21:3
@Scratchpad/1:24:11 scratchpad.js:1010:7

海未「このような動作になります」
穂乃果「よくわかんないよ~」
海未「kotori関数の中でumi関数を定義しています。それぞれの関数内で変数を1つ定義し、お互いに参照しようとしています。ここまではいいですね?」
穂乃果「うん、それはわかるよ」
海未「”チーズケーキ”が出力されているので、umi関数からkotori関数の変数は参照できています。しかし逆はReferenceErrorになっていますから、参照できていません」
ことり「中から外は見えるけど、外から中は見えないってこと?」
海未「そうです。関数内でvarもしくはletで宣言した変数は、その関数のローカル変数になります。ですから、宣言した関数の外から見ることはできないのです」

海未「これに関連して、名前衝突の考え方を見ておきましょう」

ほむまん
チーズケーキ

海未「今回は、kotoriとumiでそれぞれ同じ名前の変数favを宣言しています。このように同じ名前の変数が宣言された場合、常に内側が優先されます」
ことり「え~っと、umi関数の中でconsole.log(fav);したらほむまんで、でもその後kotori関数だとチーズケーキのままってことは・・・kotori関数のfavとumi関数のfavは違う変数なのかな?」
海未「そうです。ですからumi関数でfavの値を変えてもkotori関数のfavの値には影響しません。複雑に入れ子になった関数で偶然名前が重複しても安全と言うことですね」


argumentsオブジェクト

海未「関数に渡される引数ですが、仮引数として名前付きで受け取る方法の他に、argumentsというオブジェクトを利用する方法があります」

ほのことうみ
にこまき

海未「引数の数が変わる場合にも対応できるので、覚えておくと便利です」


クロージャ

海未「さて、それでは最難関にいきましょうか」
穂乃果「いや、ここまでも結構難しかったよ?」

海未「クロージャという概念があります。実はさきほど見た、関数内にある関数はクロージャなのですが、まあこればかりは理論より先にコードを見た方が分かりやすいでしょう」

海未「idol("Honoka Kosaka")の呼び出しによって、return nameをするだけの無名関数が返されます。まずこれはいいですね?」
穂乃果「い、一応・・・」
海未「honoka()の呼び出しでは、さきほど引数として渡した”Honoka Kosaka”という文字列にアクセスできます」
ことり「honoka()の戻り値が”Honoka Kosaka”だよね」
海未「その値は、外側の関数のローカル変数、この場合は仮引数ですから、いずれにせよidol("Honoka Kosaka")の呼び出しが終了した時点で消えてしまうはずなのに、です」
ことり「・・・あれ、そういえばそうだね」
海未「こうして、内側の関数の中に外側の関数の変数を閉じ込めるようなことができるのです」
穂乃果「これは何が嬉しいのかな?」
海未「状態を持った関数が作れる、それをなんとなくオブジェクトっぽい使い方ができる、といったところでしょうか。例えば」

Umi Sonoda
園田海未
3841
3941

海未「スクフェスの部員っぽいものですが、名前とSmile/Pure/Coolの各ステータスを持っていて、名前は変更できて、練習によってステータスが増やせる、といったことを実現しています」
ことり「え~っと・・・returnしてるのはオブジェクトリテラルだよね。関数が詰まったオブジェクトを返してるってこと?」
海未「はい。その関数を介して、外側の関数が持っている値にアクセスしています。逆に、この関数群以外によってはクロージャ内へアクセスできませんから、例えばステータスの値を外から書き換えることはできません」
ことり「直接触れないデータに対するアクセス手段がこの関数群なんだ」
海未「このあたりの考え方は、後ほど出てくるオブジェクト指向とよく似ています」
穂乃果「細かい理屈は置いておいて、なんか使い方はわかった気がする・・・」


海未「関数については、ここまでです。特にクロージャは正確に理解しようとするとなかなか難解ですから、まずは実際にコードを書きながら理解していくのがいいでしょう」
穂乃果「うーん、知恵熱・・・」
ことり「穂乃果ちゃん、あとで復習しよっか・・・」


LINEで送る
Pocket


返信を残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です