Skip to content

Latest commit

 

History

History
356 lines (253 loc) · 14.8 KB

README.md

File metadata and controls

356 lines (253 loc) · 14.8 KB

Homework Javascript Week 4

Exercises

The homework for this week can be found in the homework folder.

Exercise 1: The odd ones out

File: ex1-doubleEvenNumbers.js

Look at the following code snippet:

function doubleEvenNumbers(numbers) {
  const newNumbers = [];
  for (let i = 0; i < numbers.length; i++) {
    if (numbers[i] % 2 === 0) {
      newNumbers.push(numbers[i] * 2);
    }
  }
  return newNumbers;
}

const myNumbers = [1, 2, 3, 4];
console.log(doubleEvenNumbers(myNumbers)); // Logs "[4, 8]" to the console

The doubleEvenNumbers function returns only the even numbers in the array myNumbers and doubles them. While this code works as advertised we would like to use a more functional approach.

Let's rewrite it (or refactor it, as professional developers would call it):

  • Using the map and filter functions, rewrite the function body of doubleEvenNumbers.

Exercise 2: What's your Monday worth?

File: ex2-mondaysWorth.js

When you're a developer at a big company your Monday could look something like this:

const mondayTasks = [
  {
    name: 'Daily standup',
    duration: 30, // specified in minutes
  },
  {
    name: 'Feature discussion',
    duration: 120,
  },
  {
    name: 'Development time',
    duration: 240,
  },
  {
    name: 'Talk to different members from the product team',
    duration: 60,
  },
];

Let's assume your hourly rate is €25. How much would you earn on that day?

  • Complete the function names computeEarnings. It should take an array of tasks and an hourly rate as arguments and return a formatted Euro amount (e.g: €11.34) comprising the total earnings.
  • Use the map array function to take out the duration time for each task.
  • Multiply each duration by a hourly rate for billing and sum it all up.
  • Make sure the program can be used on any array of objects that contain a duration property with a number value.

Exercise 3: Lemon allergy

File: ex3-lemonAllergy.js

Your mom bought you a basket of fruit, because you're doing so well in HackYourFuture. How sweet of her!

const fruitBasket = [
  'apple',
  'lemon',
  'grapefruit',
  'lemon',
  'banana',
  'watermelon',
  'lemon',
];

However, she forgot that you are allergic to lemons! Let's quickly dispose of them before you get an attack.

  • Write a function
  • Use the filter array function to take out the lemons
  • Output a string that says: "My mom bought me a fruit basket, containing [array of fruits] !"

Exercise 4: Publish and Subscribe

File: ex4-publishSubscribe.js

A software pattern that you may encounter in the future is a construct called the Observer Pattern. It enables subscribers (which are usually functions) to subscribe to notifications from a publisher. Any number of subscribers can subscribe.

Consider the code below (from ex4-publishSubscribe.js):

  • The call to the createPublisher function returns an object. For ease of reference, let's call it a Publisher object here. The Publisher object has two properties, subscribe and notify, which are both functions. In this exercise you are required to complete them. (But continue reading first.)

  • As you can see below, the createPublisher function is called and the resulting Publisher object is assigned to the myPublisher variable.

  • Next, two subscriber functions are defined, notably consoleUpperCase and consoleLowerCase. A subscriber function is defined here as a function that takes a single parameter, message. It is up to the subscriber what to do with message. (The Publisher has no say in this!).

  • The subscriber functions are added as subscribers to myPublisher by calling its subscribe function. The subscribe function should take the function passed to it as an argument and push it onto the subscribers array. (Yes, you can store functions in an array. Functions are treated in JavaScript like any other value. See First-class Function in MDN Web Docs.)

  • The standard console.log function, which also conforms to the minimum requirement for a subscriber (although it can take more than one argument) is also added as a subscriber.

  • Finally, a call to the Publisher's notify function is expected to iterate through, and call, all subscribers from the subscribers array, relaying the notification message to each subscriber.

Good luck with completing createPublisher()!

function createPublisher() {
  const subscribers = [];
  return {
    subscribe(/* TODO parameter(s) go here */) {
      // TODO complete this function
    },
    notify(/* TODO parameter(s) go here */) {
      // TODO complete this function
    },
  };
}

// A candidate subscriber function
function consoleUpperCase(message) {
  console.log(message.toUpperCase());
}

// Another candidate subscriber function
function consoleLowerCase(message) {
  console.log(message.toLowerCase());
}

// Create a publisher object
const myPublisher = createPublisher();

// Register three subscribers
myPublisher.subscribe(console.log);
myPublisher.subscribe(consoleUpperCase);
myPublisher.subscribe(consoleLowerCase);

// Send a message to all current subscribers
myPublisher.notify("Let's see what happens here!");
// Let's see what happens here! (console.log subscriber)
// LET'S SEE WHAT HAPPENS HERE! (consoleUpperCase subscriber)
// let's see what happens here! (consoleLowerCase subscriber)

Exercise 5: Transfer into Wallet

Folder: ex5-wallet

In this exercise we will practice using the Chrome debugger. Other browsers, such as FireFox, Edge and Safari have similar tools but the exact look and feel may be different.

At the end of the index.js file of the exercise you will find a quiz object with multiple-choice questions that we would like you to complete as you follow along with the instructions below.

Read more about debuggers in general in the Study Guide: Debuggers

For an external tutorial on how to use the Chrome debugger, check out: The definitive guide to JavaScript Debugging [2021 Edition].

Here is a listing of the code in index.js (minus the quiz object) we will be working with:

const eurosFormatter = new Intl.NumberFormat('nl-NL', {
  style: 'currency',
  currency: 'EUR',
});

function createWallet(name, cash = 0) {
  function deposit(amount) {
    cash += amount;
  }

  function withdraw(amount) {
    if (cash - amount < 0) {
      console.log(`Insufficient funds!`);
      return 0;
    }

    cash -= amount;
    return amount;
  }

  function transferInto(wallet, amount) {
    console.log(
      `Transferring ${eurosFormatter.format(amount)} from ${name} to ${
        wallet.name
      }`
    );
    const withdrawnAmount = withdraw(amount);
    wallet.deposit(withdrawnAmount);
  }

  function reportBalance() {
    console.log(`Name: ${name}, balance: ${eurosFormatter.format(cash)}`);
  }

  const getName = () => name;

  return {
    deposit,
    withdraw,
    transferInto,
    reportBalance,
    getName,
  };
}

const walletJack = createWallet('Jack', 100);
const walletJoe = createWallet('Joe', 10);
const walletJane = createWallet('Jane', 20);

walletJack.transferInto(walletJoe, 50);
walletJane.transferInto(walletJoe, 25);

walletJane.deposit(20);
walletJane.transferInto(walletJoe, 25);

walletJack.reportBalance();
walletJoe.reportBalance();
walletJane.reportBalance();

Since this is a browser-based exercise, the file index.js will be loaded via a <script> tag in index.html. The index.html file itself requires no further consideration here.

Let's run the exercise using our convenience command npm run it:

npm run it

> [email protected] it C:\Users\jimcr\dev\hackyourfuture\homework
> node ./test-runner/run-it

? Rerun last test (1-Javascript, Week4, ex5-wallet)? No
? Which module? 1-Javascript
? Which week? Week4
? Which exercise? ex5-wallet
Running exercise, please wait...
HTTP server running at http://localhost:3030
Press Ctrl-C to exit.

This will run the exercise in the default browser (if your default browser is not Chrome then open this URL manually in Chrome: http://locahost:3030).

Next, open the Developer Tools by pressing function key F12 and examine the console output. It will look like this:

Transferring € 50,00 from Jack to undefined
Transferring € 25,00 from Jane to undefined
Insufficient funds!
Transferring € 25,00 from Jane to undefined
Name: Jack, balance: € 50,00
Name: Joe, balance: € 85,00
Name: Jane, balance: € 15,00

We seem to have a bug because we get undefined where we expect a name.

Open the Sources panel from Developer Tools. Select index.js from the explorer panel and make sure that the console output is visible in the bottom panel, as shown in Figure 1.

Dev Tools Debugger

Figure 1. The Chrome Developer Tools Debugger.

First let's examine what causes the undefined value in the message. The console.log that outputs that message starts on line 26. We would like to inspect the state of the program when the execution reaches that point. For that purpose we will place a breakpoint at line 26. A breakpoint is a location in our code where we would like the JavaScript engine to pause execution when we run the program with the debugger.

To place a breakpoint at line 26, click to the left of the number 26 in the left margin of the editor window. A blue marker will appear to indicate the presence of a breakpoint (Figure 2):

Breakpoint at line 26

Figure 2. Breakpoint placed at line 26.

With this breakpoint set, reload the page to rerun the JavaScript code. The execution will be paused at line 26, as indicated by the blue highlighting of that line:

Breakpoint hit

Figure 3. Breakpoint at line 26 is hit.

To the right of the code panel you can inspect, amongst others, Breakpoints, Scope and the Call Stack.

❓ Please answer questions q1 and q2 in the quiz object.

When we expand the variable wallet in the local scope of the Scope panel, we can see that it contains the following properties (all functions):

  • deposit
  • getName
  • reportBalance
  • transferInto
  • withdraw

There is no name property there. That must be the reason why we get undefined when we try to access wallet.name. Let's examine this by instructing the debugger to step over (i.e. execute) the console.log function call. Figure 4 below shows the debug buttons located near the upper right corner of the Developer Tools.

Debug buttons

Figure 4. Developer Tools debug buttons

Hover the mouse over each of these buttons in the browser and take note of the tooltips that appear.

❓ Please answer question q3 of the quiz object.

Press the button Step over next function call. As you can see the execution is now stopped at line 31. The whole function call, which spans (due to Prettier) four lines is executed and stepped over. If we examine the console at the bottom of the screen we can see that the variable name is displayed correctly, but wallet.name is undefined. That is, as we saw earlier in the Scope panel, because awalletobject doesn't include aname property.

The name variable, as well as the cash variable were intentionally made private to each instance of a wallet object, by means of a closure. This to avoid that external code, accidentally or with malicious intent, changes the name of the wallet owner or change its cash amount. This holds true even for other instances of wallet objects.

However, we do want to give some form of read-only access to the information these variables hold. For this purpose the wallet object provides two functions, reportBalance() and getName(), that do have access to the "closed-in" variables (or actually, in this case, parameters) name and cash. We can therefore fix the undefined bug by replacing wallet.name with wallet.getName().

Let's try and make that change in the VSCode editor window. Prettier will probably now cause the console.log call to span five lines.

Now, with the breakpoint still set at line 26, reload the page (first click the large X to cancel loading the current page and then the reload button to reload the page). Then, step over the console.log and inspect the console.

❓ Please answer question q4 of the quiz object.

With execution paused at (now) line 31, press the Step into next function call button. If all is well that should take us into the function withdraw(), which is being called from line 31. If you hover your mouse over the variables cash and amount on line 31 you can peek at their current values, respectively 100 and 50: that should be sufficient to make the withdrawal successful.

Let's add a breakpoint at line 17. That breakpoint will only be hit in the case of insufficient funds. Press the button Step over next function call. What is being stepped over here is not a function call but a statement. So maybe this button is better labelled "Step over next statement"... But we will have to make do with what we got.

In any case, the console.log of line 17 was not executed, as we expected.

With the undefined problem solved, we would now like to examine the instances where we get the message Insufficient funds!. The breakpoint at line 17 is perfect for that. But we no longer need the breakpoint at line 26. Click on that breakpoint in the left margin of the editor window to remove it again.

Now, let's resume execution of the code by pressing the button Resume script execution. Our breakpoint at line 17 will be hit. Inspect the Scope panel to determine the name of the owner of the wallet that has insufficient funds.

❓ Please answer question q5 of the quiz object.

Press the Resume script execution again. The program will now run to completion without issues.

Having answered all questions, it is now time to run a test to check whether you provided the correct answers. Run the test command with:

npm test

and select to rerun the previous exercise. Note that the test will not use the browser.

If you have provided all the correct answers you will see:

All unit tests passed.

Otherwise you will get a message in red for each incorrect answer:

*** Unit Test Error Report ***

- wallet q1: At line 26, which variables are in the scope marked Closure?
- wallet q2: What is in the Call Stack, from top to bottom?
- wallet q3: What tooltip appears when hovering over the third debug button?
- wallet q4: What is displayed in the console?
- wallet q5: The owner of the wallet with insufficient funds is:

This concludes the exercise.