この単元を通して習得すること:
- 📝 テストを書くことができる
- 🤔 テストを書くときの文法を理解する
- ✅ 自分で書いたテストがパスするようにコードを書く
- 🧠 クロージャのスコープの中にある変数の値が、何回も呼び出す中でどう変わるかを予想できるようにする
ダイレクトに答えを教えてもらうのではなく、自分自身で物事を理解するという経験を少しずつ積んでいくこともカリキュラムの一部となっています。
なぜそれが必要だと思いますか?なぜ単純に答えを教えないのでしょうか?
テクノロジーの進化は非常に速いため、特定のテクノロジーのみを教えるのが良いとは言えませんし、「あなたのためになっている」とも言えません。エンジニア 👨🏾💻👩🏻💻 として働く際にあなたが成功できるかどうかは、学習を続け、自分自身で新しいことを理解できるかどうかにかかっています。私たちはあなたのエンジニアとしての本能を磨くことで、その手助けをしたいと思っています。そこで、みなさんには次のような感性を身につけていただきたいと思います:
- Google をどのように使うべきか
- オンライン上で何を探すのか
- 良いリソースと悪いリソースをどのように区別するのか
- エラーメッセージの読み方
- 今あるコードから手がかりを推測する
this
キーワードを使用して何かを参照する場合、この this
は 常に オブジェクトです。そのオブジェクトは、今、対象としている関数に最も関連性のある "コンテキスト" だと JavaScript が考えるオブジェクトです。例えば次のような関数を呼び出すと...
let someObj = {
hmm: "something",
someField: function() {
return this;
}
};
someObj.someField();
この関数を実行した場合、someField
内の this
は someObj
を参照します。したがって、someField
の戻り値は someObj
です!
もう少しわかりやすい例を次に示します:
let someObj = {
hmm: "something",
someField: function() {
return this.hmm;
}
};
someObj.someField();
今回は、someField
の戻り値は文字列 "something"
です。
ここで、 someField
の関数宣言がアロー関数ではないことに気づきましたか?アロー関数を使うと this
が参照するコンテキストが変更されてしまいます。では、その違いを確かめるためにアロー関数を使用して関数を書き換えてみましょう。
let someObj = {
hmm: "something",
someField: () => this;
};
someObj.someField();
このコードをブラウザのコンソールに貼り付けて実行すると、this
が window
参照していることがわかると思います。this
が someObj
を完全に飛び越えてグローバル変数を参照しています。
今の段階で分かりにくくても心配はいりません。今後 this
を使った演習が出てくるので徐々に理解が深まります。今すぐ理解できないとこだわる必要はありませんが、もっと説明してほしい!という場合はいつでも #slackoverflowチャネルやAMAなどで質問してください。
この課題で作成する関数は、アロー関数の構文を使ったものではありません。また、何かを参照するために this
を使用する必要があるかもしれません。深く掘り下げる前にまず this
に慣れる良い練習となるでしょう。
これまでにクロージャをいくつか作成し、作成したテストの読み方を学んだので、ここでは通常のコードと一緒に単体テストを作成する練習を行いましょう。この課題ではまずはテストを書くところから始めるので、テストを書く機会を増やしていきましょう。問題は2つあります。
テストを書く上でのポイントを知りたい場合は、この README ファイルの一番下にある参考資料のリストをご覧ください。
- テストコードは
specs/tests.js
に記載されており、testrunner.html
をブラウザで開くことでテストを実行できるようになっています。
1. gameGenerator
:src/closures.js
の中に gameGenerator
という関数が用意されています。クロージャを使って、数字あてゲームを作成してください。既に用意されている randomInteger
関数を使って実装してください。
ゲームは以下の要件を満たしてください:
-
インプットに上限値(upper bound)を表す数値型を取ります。
-
0 から上限値(upper bound)までの間で、ランダムな数 🎲 を生成します。
-
reset
というメソッドを用意して、ゲームをリセットできるようにします。正解の値と推測した回数をリセットします。上限値はリセットしないでください。 -
giveUp
というメソッドを用意して、正解の値を返してゲームをリセットできるようにします。 -
guess
というメソッドを用意して、あなたが推測した値を入力できるようにします。推測が合っている場合は true、間違っている場合は false を返すようにします。 -
何回推測したかを記憶できるようにします。
-
numberGuesses
というメソッドを用意して、現在何回推測したかを示せるようにします。 -
テストを書き、それからテストをパスするコードを書くようにしましょう。
🔧 注意: テストファイルに書かれている
expect(false).toBeTruthy();
は仮のテストコードなので、書き換えてください。
2. accountGenerator
:src/closures.js
の中に accountGenerator
という関数が用意されています。
-
まずテストを書き、それからテストをパスするコードを書くようにしましょう。
-
1 回の取引内容を、以下のようなオブジェクトで表現してください:
const exampleDeposit = { type: "deposit", amount: 1000, before: 500, after: 1500, status: "approved", time: (Date instance) }; const exampleWithdrawal = { type: "withdrawal", amount: 1000, before: 520, after: 520, status: "denied", time: (Date instance) };
accountGenerator
関数を次のように変更してください:
-
getBalance
という関数を追加して、現在の残高を返すようにします。 -
withdraw
を変更して、取引内容を表すオブジェクトを返すようにします。 -
deposit
を変更して、取引内容を表すオブジェクトを返すようにします。 -
transactionHistory
という関数を追加して、最新のものから数えてn
個の引き出しもしくは預け入れ 💵 の履歴を参照できるようにします。例えば:
const account = accountGenerator(100); account.transactionHistory(2); // => [{...}, {...}] // この配列の要素は、[{.. 最新から数えて2個目の履歴 ..}, {.. 最新の履歴 ..}] であること
-
averageTransaction
という関数を追加して、引き出しもしくは預け入れ額の平均値 💰 を算出できるようにします。ポイント: 確定した取引のみが総額にカウントされるようにしてください! アウトプットは以下の形式のオブジェクトとします。{ deposit: number, withdrawal: number }
-
Date
オブジェクトを使い、取引内容を表すオブジェクトにはtime
をキーとして入れ、値には時間を入れます。 📅