JavaScript:

Bugs and Errors

Lecturer: Иo1lz

OUTLINE

  • Preface
  • Strict Mode
  • Mistake Finding
  • Debugging
  • Error Propagation
  • Exceptions
  • Select Catching

Preface

  • Flaws in computer programs are usually called bugs
  • We  can roughly categorize bugs into 2 caution:
    • caused by the thoughts being confused
    • caused by mistakes introduced while converting a thought to code.

JavaScript’s looseness is a hindrance.

Its concept of bindings and properties is vague enough that it will rarely catch typos before actually running the program.

true * "monkey";
NaN

Strict Mode

JavaScript can be made a little stricter by enabling strict mode

function canYouSpotTheProblem(){
    "use strict";
    for (counter = 0; counter < 10; counter++){
        console.log("Happy happy");
    }
}

canYouSpotTheProblem();

putting the string "use strict"

  • at the top of a file
  • a function body.

About "THIS"

function Person(name){ this.name = name; }
let ferdinand = Person("Ferdinand");
console.log(name);
// -> Ferdinand
"use strict"
function Person(name){ this.name = name; }
let ferdinand = Person("Ferdinand");        // forget new
// -> TypeError: Cannot set property 'name' of undefined

Finding Mistake

There some ways to find the programming mistake:

  1. Types
  2. Carpet search
  3. Automated test

Types

Add a comment before the function to describe its type

// (VillageState, Array) -> {direction: string, memory: Array}
function goalOrientedRobot(state, memory){
    // ...
}

There are several JavaScript dialects that add types to the language and check them. The most popular one is called TypeScript.

Testing

the process of writing a program that tests another program.

function test(label, body){
    if (!body()) console.log(`Failed: ${label}`);
}

test("convert Latin text to uppercase", () => {
    return "hello".toUpperCase() == "HELLO";
});
test("convert Greek text to uppercase", () => {
    return "χαιρετε".toUpperCase() == "ΧΑΙΡΕΤΕ";
});
test("don't convert case-less characters", () => {
    return "مرحبا".toUpperCase() == "مرحبا";
});

test runners

There exist pieces of software that help you build and run collections of tests by providing a language suited to expressing tests and by outputting informative information when a test fails.

the more external objects that the code interacts with, the harder it is to set up the context in which to test it

Debugging

function numberToString(n, base = 10){
    let result = "", sign = "";
    if(n < 0){
        sign = "-";
        n = -n;
    }
    do{
        result = String(n % base) + result;
        n /= base;
    }while(n > 0);

    return sign + result;
}

console.log(numberToString(13, 10));
13
1.3
0.13
0.013
…
1.5e-323
13
1
0

Instead of n /= base, what we actually want is n = Math.floor(n / base) so that the number is properly “shifted” to the right.

function numberToString(n, base = 10){
    let result = "", sign = "";
    if(n < 0){
        sign = "-";
        n = -n;
    }
    do{
        result = String(n % base) + result;
        n = Math.floor(n / base);
    }while(n > 0);

    return sign + result;
}

console.log(numberToString(13, 10));

Error Propagation

Not all problems can be prevented by the programmer

// Run on the browser
function promptNumber(question){
    let result = Number(prompt(question));
    return result;
}

console.log(promptNumber("How many trees do you see?"));
// Run on the browser
function promptNumber(question){
    let result = Number(prompt(question));
    if(Number.isNaN(result)) return null;
    else return result;
}

console.log(promptNumber("How many trees do you see?"));

In many situations, Returning a special value is a good way to indicate an error.

function lastElement(array){
    if(array.length == 0){
        return {failed: true};
    }else{
        return {element: array[array.length - 1]};
    }
}

what if the function can already return every possible kind of value?

Exceptions

When a function cannot proceed normally, what we would like to do is just stop what we are doing and immediately jump to a place that knows how to handle the problem.

This is what exception handling does.

// Run on the browser
function promptDirection(question){
    let result = prompt(question);
    if(result.toLowerCase() == "left") return "L";
    if(result.toLowerCase() == "right") return "R";
    throw new Error("Invalid direction: " + result);
}

function look(){
    if(promptDirection("Which way? ") == "L"){
        return "a house";
    }else{
        return "two angry bears";
    }
}

try{
    console.log("You see", look());
}catch(error){
    console.log("Something went wrong: " + error);
}

Cleaning up after exceptions

The effect of an exception is another kind of control flow.

Every action that might cause an exception, which is pretty much every function call and property access, might cause control to suddenly leave your code.

// Run on the browser
const accounts = {
    a: 100,
    b: 0,
    c: 20
};

function getAccount(){
    let accountName = prompt("Enter an account name: ");
    if(!accounts.hasOwnProperty(accountName)){
        throw new Error(`No such account: {$accountName}`);
    }
    return accountName;
}

function transfer(from, amount){
    // Transfers a sum of money from a given account to another
    if(accounts[from] < amount) return;
    accounts[from] -= account;
    accounts[getAccount()] += amount;
}
function transfer(from, amount){
    if(accounts[from]] < amount) return;
    let progress = 0;
    try{
        accounts[from] -= amount;
        progress = 1;
        accounts[getAccount()] += amount;
        progress = 2;
    }finally{
        if(progress == 1){
            accounts[from] += amount;
        }
    }
}

Selecting Catching

When an exception makes it all the way to the bottom of the stack without being caught:

  • In browsers, a description of the error typically gets written to the JavaScript console
  • Node.js, it aborts the whole process when an unhandled exception occurs.
for(;;){
    try{
        let dir = promtDirection("Where?");
        console.log("You chose ", dir);
        break;
    }catch(e){
        console.log("Not a valid direction. Try again.");
    }
}
class InputError extends Error {}

function promptDirection(question){
    let result = prompt(question);
    if(result.toLowerCase() == "left") return "L";
    if(result.toLowerCase() == "right") return "R";
    throw new InputError("Invalid direction: " + result);
}
for(;;){
    try{
        let dir = promptDirection("Where? ");
        console.log("You chose ", dir);
        break;
    }catch(e){
        if(e instanceof InputError){
            console.log("Not a valid direction. Try again.");
        }else{
            throw e;
        }
    }
}

Thanks for listening.

JavaScript: Bugs and Errors

By Иo1lz

JavaScript: Bugs and Errors

This is the slide that refer from "Eloquent JavaScript" chapter 8.

  • 119