Objects in JavaScript, continued

We should repeat that objects are normally defined as instances of classes. This is what we showed you. In other object oriented languages when an object is instantiated a variable is created as a copy of the class. This means that for every existing object, think of 1000s in an array, a copy of all its methods take up space in computer memory. Not quite so in JavaScript. The common concepts such as properties, and methods however, do exist. But the methods are linked to the objects instead of copied to them.

Native Objects aka Standard Built-in Objcts

We have already touched on objects related to the DOM. The DOM and the native objects of JavaScript are givens. We do not define them ourselves. The DOM is created by the browser as it renders the page. The JavaScript native objects are defined as part of the JavaScript language.

Array, Math, and Date are some of the more commonly used native objects. Their spec holds many methods that you might benefit from in your daily coding with JavaScript. Please refer to them for details. An excellent, the best, printed source is [Fla11a]. Online you should probably use https://developer.mozilla.org/en-US/docs/Web/JavaScript, or go directly to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects . Generally MDN, Mozilla Developer Network is a treasure trove of knowledge on all things web, especially, perhaps, JavaScript.

Objects in Arrays

Now we have seen object creation and manipulation of individual objects. A bit on the sparse side, the examples had but one method in stead of several or many which is more normal. In this section we shall look at an example of creating objects and placing them in arrays for more "industrial" processing.

Example 10.10. The Object Definition, Die.js
/**
 * Die object
 */
class Die {
    constructor(i, t) {
        this.snr = i;           // serialno, index
        this.type = t;          // type, 2: coin, 6: die, ...
        this.locked = false;
        this.value = 0;
        this.roll();
    }

    draw(where) {
        let d = document.createElement("div");
        d.setAttribute("class", "die");
        d.setAttribute("id", "die" + this.snr);
        if (this.locked) {
            d.style.backgroundColor = "yellow";
        }
        let t = document.createTextNode(this.value);
        d.appendChild(t);
        d.addEventListener('click', lockFlipFlop);
        $(where).appendChild(d);
    }

    roll() {
        this.value = rollit(this.type);
    }

    lock() {
        this.locked = true;
    }

    unlock() {
        this.locked = false;
    }

    isLocked() {
        return this.locked;
    }
}

We see the Die defined, in the constructor, with some properties and methods. The properties will be given values on instantiation thus giving state to the object. The methods will be linked to each object built on the Die class.


The Die class is general, and may be used in any application playing with dice. In one concrete case, yatzy, it may be used as follows.

Example 10.11.  Fragment of OO Yatzy Application, yatzyoo0.js
/*
 * function to start game
 * called on page load
 */
const start = function() {
    dice = [];
    tries = TRIES;

    for (let i = 0; i < ANTAL; i++) {
        let die = new Die(i, 6);        // instantiates Die object
        dice.push(die);                 // store it the array
        die.draw("board");              // and paints it on the screen
    }

    $("rollem").addEventListener("click", function() {
        if (tries > 0) {
            rollDice();
            redrawBoard("board");
            tries -= 1;
        } else {
            score();                    // must record score
                                        // should prevent locking
                                        // and prevent further score
                                        // until after rollDice
            tries = TRIES;
        }
    });
}

const ANTAL = 5;    // number of dice
const TRIES = 3;    // number of tries
var tries;          // counter of tries, initialized in start
var dice;           // dice array, initialized in start
window.addEventListener("load", start);

The constants ANTAL and TRIES are defined in order to avoid spreading magic numbers around the code. In addition two global variables are defined. The array dice defined here to be global will be initialized in the load eventhandler start. The variable tries control the number of rolls in each play.

The eventhandler start initializes the variables as already mentioned. Then it, industrially, creates a number of Die objects placing each of them in the dice array. You will look aside into the init method of Die and see that the die is rolled as part of initialization. The you put the die on the screen.

The bottom part of start creates an eventlistener listening to the clicking of the Roll button of the HTML5 page. The eventhandler is built in as an immediate function. The code of the eventhandler does not execute when start runs. It runs asynchronously whenever the event triggers.

The handler rolls the dice by running a function. The dice are then painted onto the screen. This is done by clearing the dice and repainting them. These two functions are shown below.


Example 10.12. Another Fragment of yatzyoo0.js
/*
 * Loop through the array of dice
 * drawing each one on the screen
 * with method defined in object
 */
const redrawBoard = function(foo) {
    clrBoard(foo);
    for (let die of dice) {
        die.draw(foo);
    }
}

/*
 * Loop through the array of dice
 * rolling each die not locked
 * with method defined in object
 */
const rollDice = function() {
    for (let die of dice) {
        if(!die.isLocked()) {
            die.roll();
        }
    }
}

Click here for the action!


Example 10.13. A Different For Loop

You may have noticed

for (let die of dice) {
    if(!die.isLocked()) {
        die.roll();
    }
}

this is new, ES6, and equivalent to

for (let i = 0; i < ++i) {
    if(!dice[i].isLocked()) {
        dice[i].roll();
    }
}

The new variant is useful and clearer than the old, and may be used if you have no need for the index.


Inheritance

An OO conventional phenomenon inheritance means that the a subclass inherits, and adds to the behaviour of the superclass. Here we have constructed an illustrative example.

Example 10.14. Inheritance, Person/Student
'use strict';
/**
 * Person object
 */

export class Person {
    constructor(name, age, weight, sex) {
        this.name = name;
        this.age = age;
        this.weight = weight;
        this.sex = sex;
    }

    getName() {
        return this.name;
    }

    setName(name) {
        this.name = name;
    }

    setWeight(w) {
        this.weight = w;
    }

    toString() {
        let s = 'I am a person: My name is: ' + this.getName();
        if (this.sex !== 'F') {
            s += ', age: ' + this.age;
            s += ', weight: ' + this.weight;
        }
        return s;
    }
}

            
'use strict';
/**
 * Student object is a Person object plus something
 */
import {Person} from './nmlPersonC2.js';

export class Student extends Person {

    constructor(name, age, weight, sex, program) {
        super(name, age, weight, sex);
        this.program = program;
    }

    getProgram() {
        return this.program;
    }

    setProgram(prog) {
        this.program = prog;
    }

    toString() {
        let s = super.toString();
        s += '. I am a ';
        s += this.getProgram();
        s += ' student.';
        return s;
    }
}

            
<!doctype html>
<html language="en">
    <!-- nmlPersonOO1.html -->
    <head>
        <meta charset="utf-8"/>
        <title>Objects, OLOO</title>
        <script src="nmlPersonC2.js"></script>
        <script src="nmlStudentC2.js"></script>
    </head>
    <body>
        <h1>Inheritance Example</h1>
        <p>
            Create a Student object based on Person.
            Add a couple of methods and demonstrate usage.
        </p>
        <p>
            Please press Ctrl->Shift->I to read the console log.
        <div>
            <script type='module'>
                import {Student} from './nmlStudentC2.js';
                import {Person} from './nmlPersonC2.js';
                
                let st1 = new Student('Adelaide', 32, 56, 'F', 'Webdev');  // give st1 state
                st1.setName('Zelda');               // test setter
                st1.setProgram('WebDev');           // test setter
                console.log(st1.toString());        // print again

                let st2 = new Student('Bruce', 22, 200, 'M', 'Webdev');    // give st2 state
                st2.setProgram('WebDev');           // test setter
                console.log(st2.toString());        // print again

                let st3 = new Person('Bruce', 22, 200, 'M');                // give st3 state
                console.log(st3.toString());        // print again
                console.log(st3);                   // print again          // no auto toString

            </script>
        </div>
    </body>
</html>

            

View in browser.