Debugging

Your last method to add to the Truck.prototype object is printOrders. This method will get an array of all of the customer email addresses, iterate through the array, and console.log the order information.

The code for this method is very similar to other functions and methods you have already written. But it will start out with a bug, which you will find using Chrome’s debugging tools.

Let’s take this step by step. Start by creating the basic version of printOrders in truck.js. In the body, you will retrieve all the coffee orders from the db object. Then you will use the Object.keys method to get an array containing the email addresses for the orders. Finally, you will iterate through the email address array and run a callback function for each element in the array.

...
  Truck.prototype.deliverOrder = function (customerId) {
    console.log('Delivering order for ' + customerId);
    this.db.remove(customerId);
  };

  Truck.prototype.printOrders = function () {
    var customerIdArray = Object.keys(this.db.getAll());

    console.log('Truck #' + this.truckId + ' has pending orders:');
    customerIdArray.forEach(function (id) {
      console.log(this.db.get(id));
    });
  };

  App.Truck = Truck;
  window.App = App;

})(window);

Inside the new printOrders method, you call this.db.getAll to retrieve all the orders as key/value pairs and pass them to Object.keys, which returns an array containing only the keys. You assign this array to the variable customerIdArray.

When you iterate through this array, you pass a callback to forEach. In the body of that callback, you try to get the order associated with an id (the customer email address).

Save and return to the console. Create a new instance of Truck and add some coffee orders. Then try your new printOrders method.

var myTruck = new App.Truck('007', new App.DataStore());
myTruck.createOrder({ emailAddress: '[email protected]', coffee: 'earl grey'});
myTruck.createOrder({ emailAddress: '[email protected]', coffee: 'decaf'});
myTruck.createOrder({ emailAddress: '[email protected]', coffee: 'double mocha'});
myTruck.printOrders();

Instead of a list of the coffee orders, you will see the error Uncaught TypeError: Cannot read property 'db' of undefined (Figure 8.15).

Figure 8.15  Error when printOrders is run

Error when printOrders is run

This is one of the most common errors that you will see when writing JavaScript. Many developers find it especially frustrating because it can be hard to pinpoint the cause. Knowing how to use the debugger, as you are about to do, is key to locating the problem.

Locating bugs with the DevTools

Debugging requires you to reproduce the error as you progressively isolate the buggy code. The Chrome debugger makes this process (almost) enjoyable.

When an error occurs, the console shows you the filename and the line number of the code that caused the error. (In Figure 8.15, the reference is to truck.js:30; your line number might be different.) Click that text to open the offending line of code in the debugging tools (Figure 8.16).

Figure 8.16  Viewing the error in the debugging tools

Viewing the error in the debugging tools

You are now viewing the sources panel of the DevTools. Click the red icon in the problem line to see the error information (Figure 8.17).

Figure 8.17  Error line called out in the sources panel

Error line called out in the sources panel

This error message indicates that the browser thinks you are trying to read a property named db, but that the object it belongs to does not exist.

The next step is to run the code just up to the line that is causing the error and then check the value of that object. In the sources panel, click the line number to the left of the line with the error flag. This sets a breakpoint for the debugger, telling the browser to pause just before it tries to run this line. When you set a breakpoint, the line number on the left turns blue and an entry is added to the breakpoints panel on the right (Figure 8.18).

Figure 8.18  Setting a breakpoint

Setting a breakpoint

Press the Escape key to show the console at the bottom of the sources panel (Figure 8.19). This is also known as the drawer. You will need to be able to see the code in the sources panel and interact with the console at the same time.

Figure 8.19  Showing the console drawer

Showing the console drawer

Run myTruck.printOrders(); again in the console. The browser will activate the debugger, and your code will pause at the breakpoint (Figure 8.20).

Figure 8.20  Debugger paused at breakpoint

Debugger paused at breakpoint

When the debugger pauses, you have access to all of the variables that are available at that point. Using the console, you can check the values of the variables, looking for signs of trouble.

Try to reproduce the error by evaluating parts of the line of code with the error flag. Start with the code that is nested furthest inside of any parentheses. In this case, that is the id variable. When you enter that on the console, it reports that the value is [email protected] (Figure 8.21).

Figure 8.21  Inspecting the innermost value

Inspecting the innermost value

Because that did not reproduce the error, try the code just outside that set of parentheses, this.db.get(id). Evaluate it on the console. You should see that the error is reported (Figure 8.22).

Figure 8.22  Reproducing the error

Reproducing the error

Now you can further isolate the cause. Begin evaluating that same piece of code, but remove parts of it, starting from the right. You will do this until the error is no longer printed. Start with this.db.get. After that, enter this.db. The console continues to report the error (Figure 8.23).

Figure 8.23  The search continues

The search continues

Finally, enter this. You are now at the point where the error is not happening (Figure 8.24).

Figure 8.24  Trimming down the code to find the cause of the error

Trimming down the code to find the cause of the error

Why does this have the value undefined inside of your callback? Inside of a callback function, this is not assigned to an object. You need to explicitly assign one.

This situation is different from your Truck.prototype methods, where this refers to the instance of Truck. Even though the callback is inside of Truck.prototype.printOrder, it has its own this variable, which is not assigned to a value and is therefore undefined.

Before fixing your code, you should be familiar with two other ways you could have located the bug. If you mouse over the different parts of the code in the sources panel, the debugger will show you their values. With the mouse over this, it shows you that its value is undefined (Figure 8.25).

Figure 8.25  Hovering the mouse reveals values

Hovering the mouse reveals values

To the right of the code is the scope panel, which contains a list of variables available. You can see that values for id and this are shown – and, again, that this is undefined (Figure 8.26).

Figure 8.26  Variable values shown in scope panel

Variable values shown in scope panel

Click the blue Variable values shown in scope panel button at the top of the right hand panel (Figure 8.27). This unpauses your code, allowing it to resume execution.

Figure 8.27  Debugger control panel

Debugger control panel

Before moving on, remove the breakpoint by clicking the blue line number again. The blue indicator will disappear (Figure 8.28).

Figure 8.28  Click the line number to toggle a breakpoint

Click the line number to toggle a breakpoint

Now, it is time to fix that pesky bug!

Setting the value of this with bind

In JavaScript, the keyword this inside of a function is automatically assigned a value when you call that function. For constructor functions and for prototype methods, the value of this is the instance object. The instance is called the owner of the function call. Using the keyword this gives you access to the properties of the owner.

As we said earlier, for callback functions this is not automatically assigned to an object. You can manually specify what object should be the owner by using a function’s bind method. (Remember that JavaScript functions are actually objects and can have their own properties and methods, such as bind.)

The bind method accepts an object argument and returns a new version of the function. When you call the new version, it will use the object argument passed in to bind as the value of this inside of the function’s body.

Inside the forEach callback, this is undefined because the callback has no owner. Fix that by calling bind and passing it a reference to the Truck instance.

Add the call to bind in truck.js.

...
  Truck.prototype.printOrders = function () {
    var customerIdArray = Object.keys(this.db.getAll());

    console.log('Truck #' + this.truckId + ' has pending orders:');
    customerIdArray.forEach(function (id) {
      console.log(this.db.get(id));
    }.bind(this));
  };
...

Outside the body of the forEach callback, the keyword this refers to the Truck instance. By adding .bind(this) immediately after the anonymous function – but inside the parentheses for the forEach call – you are passing forEach a modified version of the anonymous function. This modified version uses the Truck instance as its owner.

Save and confirm that the orders are printed correctly. You will need to re-declare myTruck and run createOrder again.

Your output should look like Figure 8.29.

Figure 8.29  printOrders works after using bind(this)

printOrders works after using bind(this)
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.176.5