Welcome!
Everything is fine.

OOP in JS

Today, we'll focus on how to do Object-Oriented Programming (OOP) in JavaScript.

Next time: forms, this and a bit on debugging.

Plan

  1. Announcements
  2. Bank Accounts
  3. OOP in JS
  4. Objects as Database
  5. APIs
  6. Your Questions
  7. Breakout
  8. Next time: Pitfalls of this

Announcements

  1. Grading status
  2. video on debugging.
  3. Sliding Tiles

OOP in JS

  • First, we'll see a practical example, to help make things concrete.
  • Then we'll talk about some abstract principles

Example of OOP in JS using new class syntax

Let's see an example using the new class syntax for OOP in JS, namely bank accounts. Each bank account has

  • state: the current balance for that account, and
  • behavior (methods): the way that clients can change the balance, namely deposit and withdrawal
class Account {
    balance = 0;
    constructor (init) {
        this.balance = init;
    }
    deposit (amount) {
        this.balance += amount;
    }
    withdrawal (amount) {
        if( this.balance >= amount ) {
            this.balance -= amount;
        } else {
            throw new Error("Insufficient funds");
        }
    }
}
var harry = new Account(1000);
var ron = new Account(2);
var hermione = new Account(200);

Here is a demo using the new class syntax; demo opens in a new tab.

If you open the JS console, you can look at the three variables harry, ron and hermione. You can try using some methods:

// Harry gives Ron 10 galleons
harry.withdrawal(10);
ron.deposit(10);
// create an account for Cho Chang
cho = new Account(200);
cho.withdrawal(50); // money for a new broom

A getBalance() Method

How could we write a method to return the current balance? Do this as a paper-and-pencil exercise.

class Account {
    balance = 0;
    constructor (init) {
        this.balance = init;
    }
    deposit (amount) {
        this.balance += amount;
    }
    ...
    getBalance () {
        return this.balance;
    }
    ...
}

OOP in JS

What we saw above has constructors, instance variables and methods. Let's briefly recap each.

Constructors

The constructor is the Account function:

  • Create a constructor function (really, it's an initializer)
  • By convention, name it with a capital letter, to remind us to use new
  • Use the new keyword when invoking the constructor
  • The value of this is an new, empty, object
  • The constructor can store stuff in this when it executes
  • The constructor has no return value
  • Commonly, it's used to initialize the instance variables

We can think of this function as a factory, but in practice we name it with the things it makes. So, we don't call it makeAccount; we called it Account because we'll use it like this:

var hermione = new Account(200);

Instance Variables

In our bank account example, the instance variable is the balance property. Instance variables

  • are also called
    • attributes
    • data members
    • fields
  • are properties of the object (this)
  • can be modified like any object property

As an aside:

  • ideally, should not be used outside the object, but
  • JavaScript doesn't have that abstraction barrier, though
  • if every object were a closure, it would

Methods

In our bank account example, the two methods were deposit and withdrawal

  • also called
    • member functions
    • instance methods
  • inherited by all objects created using the given constructor
  • the function is shared by all instances (saving memory)
  • can use this to refer to the object and its properties

Using Objects as a Database

There are many kinds of database. A very common and useful type is a key-value database.

For an in-memory database of that kind, we could use JavaScript objects. Like this:

var house = {};
house['cho'] = 'ravenclaw';
house['draco'] = 'slytherin';
house['cedric'] = 'hufflepuff';
console.log(house['cho']);

Or this:

var heads = {};
heads['gryffindor'] = 'McGonnagall';
heads['slytherin'] = 'Snape';

APIs

Abstraction barriers give the implementation freedom and flexibility. Here's a picture of the idea (picture by Ross Anderson):

A client and two Database Implementations
The code on the left (the client) needs a database. On the right, there are two different databases to choose from, and the client could use either one, because they implement the same API.

In this chapter we want a key-value database and we decided to use objects, but what if we wanted to change our minds? What if we wanted to switch to Oracle NoSQL or LMDB or ... But if we hide the database behind an abstraction barrier, we can isolate that implementation decision and allow ourselves to change it.

Our API will consist of two methods:

  • put(key,val) stores a key, value pair
  • get(key) returns the stored value

Here's one way, using the new class syntax:

class KeyValueDB {
    db = null;
    constructor () {
        this.db = {};
    }
    get(key) {
        return this.db[key];
    }
    put(key,val) {
        this.db[key] = val;
    }
}

Finally, let's see an example of it in use:

// make a new database of heads-of-house
var heads = new KeyValueDB();

// store two values
heads.put('gryffindor', 'McGonnagall');
heads.put('slytherin','Snape');

Questions

We'll look at your questions

Coffeerun

I'll run it and do:

  • look at debug.ds1
  • fill out the form and submit
  • look at debug.ds1 again
  • try printing via
    • debug.truck1.printOrders_buggy
    • debug.truck1.printOrders_closure
    • debug.truck1.printOrders_bind
    • debug.truck1.printOrders_arrow

Exercise 1

To get started, copy the ~cs204/pub/downloads/bank folder to your cs204 folder:

cd ~/public_html/cs204/
cp -r ~cs204/pub/downloads/bank bank
  1. Make sure it works as above
  2. Add an instance variable to the object for different types of accounts: checking vs savings

Here's my solution:

function Account(type,init) {
    this.type = type;
    this.balance = init;
}

Exercise 2

Write code to add a method that will add x percent interest to a savings accounts but not to checking accounts. Name it addInterest.

Here's my solution:

class Account {
    constructor (init) {
        this.balance = init;
    }
    deposit (amount) {
        this.balance += amount;
    }
    ...
    addInterest (rate) {
        if( this.type == "savings" ) {
            this.balance += rate * this.balance;
        }
    }
}

Did you rename the "x" parameter? Why or why not?

Breakout

  • work on homework
  • ask 1:1 questions