LoginSignup
415
240

More than 3 years have passed since last update.

4歳娘「パパ、そんなときはクロージャが役に立つんじゃない?」

Last updated at Posted at 2020-04-12

↓新しい記事もよろしくやで!

休日ワイ

ワイ「(カタカタカタカタ・・・・・・)」

娘(4歳)「パパ、お休みの日までお仕事してるの?」

ワイ「ん?これはお仕事とちゃうで」
ワイ「ワイのオリジナルポエムを販売するための」
ワイ「ポエム販売サイトを作ってんねん」

娘「Qiitaに書き溜めたポエムを、とうとう有料で販売するんだね!」

ワイ「誰の記事がポエムやねん
ワイ「これはれっきとした技術記事や」

娘「そっか」

ワイ「そうやなくてな」
ワイ「ワイが作った、心温まるポエムを販売して」
ワイ「それで生計を立てて」
ワイ「働かんでええようにするんや!」

娘「パパ、すごい!」

ワイ「ぐへへ」
ワイ「まあ、下手に仕事を辞めたりしたら」
ワイ「路頭に迷ってしまうかもしれんから」
ワイ「ちょっと不安やけどな・・・」

娘「パパならきっと大丈夫だよ!」

ワイ「ありがとうな、娘ちゃん」
ワイ「よっしゃ、今日もガンガン開発していくで!」

今から開発する機能

ワイ「今日は、ボーナスポイント機能を作るんや!」

娘「ボーナスポイント機能?」

ワイ「せや」
ワイ「このポエム販売サイト会員登録をしてくれたお客様は」
ワイ「ポエムを購入するたびにポイントをゲットできるんや」
ワイ「そのポイントを溜めて、またポエムを買うことができるんや」

娘「画期的なシステムだね!」

ワイ「せや」
ワイ「発明やでこれは」
ワイ「しかもやな」
ワイ「毎月10日にサイトを訪れたお客様は」
ワイ「ボーナスポイントとして3ポイントがもらえるんや」

娘「すごーい」

ワイ「1ポエムが360円やから・・・」

娘「たった10年で1ポエムもらえちゃう感じだね!」

ワイ「せやな!」
ワイ「でもな・・・」
ワイ「今そのボーナスポイント機能のことで行き詰まってんねん」

娘「どんな風に行き詰まってるの?」

ワイ「ええとな」
ワイ「画面上に、ボーナスポイントをGET!って書いてあるボタンがあるやろ?」

娘「うん」

ワイ「このボタンをクリックすると」
ワイ「JavaScriptgetBonusPointっていう関数が実行されて」
ワイ「それによって会員さんのポイントが増える仕組みなんやけど」

娘「うんうん」

ワイ「現状、何回もクリックすると無限にボーナスポイントがもらえてしまうんや」

娘「そっか・・・」
娘「ちょっとお客様ファースト過ぎるね」

ワイ「そうなんよ・・・」

1回だけgetBonusPointしたい

ワイ「要は、getBonusPoint1回しか実行できない・・・」
ワイ「そんな感じにしたいんや」

娘「じゃあ・・・」
娘「こんな感じにすればいいんじゃない?」

JavaScript
// ボーナス取得済みかどうかのフラグ
let flag = false;

const getBonusPoint = (member) => {
    if (flag) {
        alert("アカン!");
    } else {
        member.point += 3;
        flag = true
    }
};

ワイ「なるほどな」
ワイ「ええと・・・?」

娘ちゃんのコードを読んでみる

ワイ「まずは・・・」

JavaScript
// ボーナス取得済みかどうかのフラグ
let flag = false;

ワイ「↑ここでボーナス取得済みかどうかのフラグを定義してるんやね」
ワイ「初期値はfalseか」
ワイ「ほんで・・・」

JavaScript
const getBonusPoint = (member) => {
    if (flag) {
        alert("アカン!");
    } else {
        member.point += 3; // ポイント付与
        flag = true
    }
};

ワイ「↑これがgetBonusPoint関数やな」
ワイ「flagtrueの場合は」
ワイ「既にボーナスポイント取得済みってことやから」
ワイ「"アカン!"ってアラートを出すんやな」

娘「そうそう」

ワイ「逆にflagfalseの場合は」
ワイ「会員さんのポイントを3増やしてあげて」
ワイ「そのあとflagtrueにする」
ワイ「つまり、フラグをボーナスポイント取得済み状態に変更するってことか」
ワイ「なるほど・・・」
ワイ「こうなってれば、1回しかgetBonusPointは実行されへん訳やな」

試してみる

ワイ「一応試してみよか」

JavaScript
// たかし君という会員は0ポイントを持っている。
const member = {
    name: "たかし",
    point: 0
}

// getBonusPointを2回実行。
getBonusPoint(member);
getBonusPoint(member);

// そのあと、たかし君のポイントを確認してみる。
console.log(member.point);

ワイ「おお、ちゃんと"アカン!"ってアラートが表示されたし」
ワイ「たかし君のポイントも3や」
ワイ「1回しか増えてへんわ」
ワイ「大丈夫そうやな」

でも、なんか不安

ワイ「でも・・・」
ワイ「コードのどこかで間違って」

JavaScript
flag = false;

ワイ「↑みたいなコードを、もしも書いてしまったら・・・」
ワイ「またボーナスポイントを取得できてしまうわけか・・・」
ワイ「なんか、一抹の不安が残るな・・・」
ワイ「flagが、丸出しのグローバル変数なのが怖いわ・・・」
ワイ「flagはもっと、自由に触れない変数であってほしいわ・・・」

娘「パパ、そんなときはクロージャが役に立つんじゃない?

ワイ「クロージャ・・・」
ワイ「・・・将来のワイのことか?」

娘「それは浮浪者でしょ!」

ワイ「おお、せやった」

よめ太郎「(けっきょく路頭に迷う想定なんかい)」

娘「そうじゃなくて、私が言ってるのはクロージャ!

クロージャって何やっけ

娘「クロージャっていうのは」
娘「関数と、その関数が宣言された環境組み合わせだよ」

ワイ「ふーん」
ワイ「全く分からんけど、その『関数と環境の組み合わせ』のことをクロージャって呼ぶんや?」

娘「そう」
娘「言い換えると、環境を持ってる関数だね」

ワイ「環境を持ってる・・・?」

娘「そう」

ワイ「よう分からんから・・・」
ワイ「コードを書きながら説明してもらえる・・・?」

娘「そうだね」
娘「まずは、さっきのgetBonusPoint作るための関数を書くの」

ワイ「関数を作るための関数・・・?」

娘「そう」
娘「書いていくね」

JavaScript
const makeFunction = () => {

};

娘「↑関数を作るための関数だから」
娘「名前はとりあえずmakeFunctionにしておくね」

ワイ「ほうほう」

娘「で、そのmakeFunctionの中に」
娘「さっきのflagっていうフラグと」
娘「getBonusPoint関数を入れちゃうの」

JavaScript
const makeFunction = () => {
    let flag = false;
    const getBonusPoint = (member) => {
        if (flag) {
            alert("アカン!");
        } else {
            member.point += 3;
            flag = true
        }
    };
};

ワイ「おお、全部入れてまうんや」

娘「そう」
娘「でも、これだけだとflaggetBonusPointも」
娘「ローカルスコープの中に閉じ込められて
娘「どこからもアクセスできなくなっちゃうから」
娘「makeFunction関数の最後の部分で」
娘「戻り値としてgetBonusPoint関数を返してあげるの」

JavaScript
const makeFunction = () => {
    let flag = false;
    const getBonusPoint = (member) => {
        if (flag) {
            alert("アカン!");
        } else {
            member.point += 3;
            flag = true
        }
    };
    return getBonusPoint; // <- 関数を返す。
};

ワイ「ほうほう」
ワイ「要はmakeFunction関数は」
ワイ「getBonusPoint関数を作って、返してくれる関数やね」

娘「そう」
娘「こうすることで、変数flagが」
娘「関数の中の変数・・・つまりローカル変数になるでしょ?」

ワイ「おお、せやな」
ワイ「ローカル変数やから、関数の外からはアクセスできんくなるわけか・・・!」

娘「そうそう」
娘「その次はね・・・」

JavaScript
const func = makeFunction();

娘「↑こんな風に、makeFunctionで作った関数を受け取るの」

ワイ「ほうほう」

娘「どうせなら、同じgetBonusPointっていう名前で受け取るね」

JavaScript
const getBonusPoint = makeFunction();

ワイ「おお、funcやとアレやしね」

娘「こうすることで」
娘「flagは関数の中に閉じ込められて
娘「ローカル変数になったから」
娘「外部からアクセスできなくなった」

ワイ「せやな」

娘「でも、getBonusPoint関数からだけは」
娘「フラグをfalseに変更することができる・・・」

ワイ「おお・・・」

娘「つまり、他の誰からも触れない・・・」
娘「自分だけが変えられるフラグを持った関数ができたの」

ワイ「おお・・・!」
ワイ「さっき・・・」

娘「言い換えると、環境を持ってる関数だね」

ワイ「・・・って言うてたのは、そういうことか」
ワイ「自分だけがアクセスできて・・・
ワイ「自分だけが変えられる・・・
ワイ「そんな閉じた環境を持ってる関数やな」

娘「そう」

ワイ「なるほどなぁ」
ワイ「初めは・・・」

娘「クロージャっていうのは」
娘「関数と、その関数が宣言された環境組み合わせだよ」

ワイ「↑この関数と環境の組み合わせって言葉の意味が、全く分からんかったけど」
ワイ「何となく分かってきたで・・・!」
ワイ「クロージャ、素敵やん・・・!!!

娘「関数の中、つまりローカルスコープに変数と関数を閉じ込めることで」
娘「他からはアクセスできないけど、その関数からだけはアクセスできる変数を作る」
娘「そして関数の方だけは、外からも実行できるように戻り値として返してやる」

ワイ「おお・・・」
ワイ「なんか、ドラえもんのタイムマシンの穴が閉じる前に」
ワイ「穴の中からリモコンを投げてもらって
ワイ「閉じ終わったタイムマシンの穴の中と通信できるみたいな」
ワイ「そんな感じやね」

娘「す、すごい例えだね」
娘「まあタイムマシンは関係ない気もするけど」
娘「穴が閉じてもリモコンが残るみたいなイメージは近いかもね」

ワイ「せやろ!」
ワイ「・・・でも、なんでクロージャっていう名前なんやろ?」

娘「うーん」
娘「変数と関数をローカルスコープに閉じ込めることで形成されるものだからじゃない?」
娘「Closureって、閉鎖とか閉塞って意味だからね」
娘「日本語では関数閉包なんて言うみたいだけど」

ワイ「なるほどなぁ・・・」
ワイ「ありがとうやで、娘ちゃん!!!」

まとめ

  • クロージャは、ローカルスコープという閉じた環境で作られた関数。
  • クロージャは、自分が作られたとき周囲にあった環境にアクセスすることができる。
  • クロージャは、同じローカルスコープにあった変数にアクセスできる唯一の手段になる。
  • 関数をクロージャにすることで、その関数からのみ変えられるフラグを持たせることができた。
  • もっと他にも色々できそう。

ワイ「↑こんな感じやね!」

娘「そうだね」
娘「でも・・・」
娘「なんかフロントエンドだけで会員のポイントを増やしたりして」
娘「意味わかんないから、このシステム作り直した方がいいよ

ワイ「・・・いや早く言わんかい!

〜おしまい〜

おまけ

娘「まあ、クロージャの使い方が学べたからいいじゃん」

ワイ「せやな・・・」

新しい記事もよろしくやで!

参考文献

415
240
8

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
415
240