INFO-I400 Topics in Informatics

Graphics, Animation, and Multimedia for the Web

Lesson 7: JavaScript Objects

Prerequisite: 4 JavaScript Functions

Reading Assignment

Creating Objects

In Python and Java, we learned that objects are bundles of data and functions and belong to classes. We created objects in Python by calling their class (constructor):

cus = Customer("James", "Henley") # Python

In Java we also needed the keyword new:

Customer cus = new Customer("James", "Henley"); // Java

In JavaScript, objects are much simpler because they don’t, intrinsically, belong to classes.1 An object in JavaScript is just a collection of properties, which are (key, value) pairs.

We’ve studied something like this before, too: remember Python dictionaries? A dictionary (also called a hash table) is a collection of (key, value) pairs:

cus_d = {"firstname": "James", "lastname": "Henley"} # Python

In this example, "firstname" and "lastname" are the keys, and "James" and "Henley" are their values.

So that’s it: JavaScript objects are just hash tables. We can write them similarly in JavaScript, using object literal notation:2

var cus = {"firstname": "James", "lastname": "Henley"}; // JavaScript

In most cases the property names (keys) do not need to be enclosed in quotation marks:

var cus = {firstname: "James", lastname: "Henley"}; // JavaScript

However, in some cases, quotation marks are required, so it may be best to get into the habit of using them.

We’ve just seen code examples in three languages; for the remainder of this lesson, we will focus on JavaScript.

In JavaScript, we can also create a “bare” object with no properties at all — we might add some properties to it later:

var bare = {};

A bare object can also be created like this, but I generally won’t be doing it this way:

var bare_o = new Object();

Accessing an Object’s Properties

We can access an object’s properties using either the dot notation, object.propname, or the subscript notation, object["propname"]. Both of these notations work on the left side of an assignment statement (for updating or creating the property) and on the right:

bare.weight = 350; // adds "weight" property with value 350
bare["height"] = 72; // adds "height" property with value 72

cus.firstname = "Jack"; // changes "firstname" property to "Jack"
cus["firstname"] = "Jill"; // changes "firstname" property to "Jill"

console.log(cus.firstname); // prints "Jill"
console.log(bare["height"]); // prints 72

To remove a property from an object, use the keyword delete:

delete bare.height; // remove "height" property
delete bare["weight"]; // remove "weight" property

Looping

A version of the for statement can loop through all the properties:

for (var prop in cus)
    console.log("The " + prop + " of cus is " + cus[prop]);

Output:

The firstname of cus is Jill
The lastname of cus is Henley

Methods

Since functions are first-class objects, we can stow them into objects as properties — and that makes them methods, since a method is just a function that belongs to an object:

bare.add = function (x, y) {return x + y};  // a function property (method)
var cust2 = {name: "Jake", add: function (x, y) {return x + y}};

The values of bare.add(5, 2) and cust2.add(5, 2) would both be 7.

A method can use the keyword this as in Java, to refer to the object that the method belongs to:

cus["fullname"] = function () { 
    return this.firstname + " " + this.lastname};

We can call the method as cus.fullname(), getting the value "Jill Henley".

Creating Objects More Conveniently

While we can always create an object using object literal notation, it would be convenient to avoid typing the property names for each object created. So if we’re creating several objects of the same “kind” (class), we can define a function to make it easier. It also helps if the objects have methods — of course, if they’re the same kind of object, they have the same methods — we can define the method as an inner function:

function makeCustomer (firstname, lastname) {
  function fullname () { return firstname + " " + lastname };
  return {"firstname": firstname, "lastname": lastname,
          "fullname": fullname};
}

var cus1 = makeCustomer("Alex", "Hamilton");
var cus2 = makeCustomer("Aaron", "Burr");

While this works fine for me, if you want to do it in a way that looks more like Java, you can also use the keyword new with a constructor. Read on.

Constructors

Constructors are called by the new operator followed by the constructor name. For example, instead of makeCustomer we could define Customer:

function Customer (firstname, lastname) {
  function fullname () { return firstname + " " + lastname };
  this.firstname = firstname;
  this.lastname = lastname;
  this.fullname = fullname;
}

var cus3 = new Customer("Thomas", "Jefferson");
var cus4 = new Customer("John", "Adams");

The new Customer(...) expression first creates a bare object, then initializes it using the Customer function (constructor), and returns the new object (the value of this).

Personally, I feel the definition of Customer is more round-about; I prefer the more concise makeCustomer.


Math

There is a Math object, similar to the Math class in Java.

Its non-function properties include:

Math.PI
Math.E  // base of natural logarithms

Its function properties (methods) include:

Math.random() // between 0.0 and 1.0
Math.floor(x)
Math.ceil(x)
Math.round(x) // to nearest integer
Math.pow(base, expt)
Math.abs(x)
Math.sqrt(x)
// Trigonometric functions, angle r in radians
Math.sin(r)
Math.cos(r)
Math.tan(r)
Math.atan(x)

Example 07-01

Animate a cannon ball which is fired from the ground, flies through the air, and falls back to the earth.

(I’m not war-mongering, because we’re not shooting at anyone.)

Before programming the solution, we need to consider the physics of the cannon ball.

Physics

We will assume:

  1. The ball is fired from a position (x0, y0) which is near the left bottom corner of the canvas, on the earth’s surface, at an angle (elevation) θ measured counterclockwise from the direction of the x axis.

  2. The ball is fired at an initial speed s.

  3. The ball is accelerated downward by the force of gravity, ay = g = −9.8 meters/second2, until it strikes the earth and stops.

  4. There is no friction as the ball moves through the air, so that the horizontal acceleration ax = 0; but the ball suddenly stops when it strikes the earth.

The variables of interest are the ball’s position (x, y) and its velocity (vx, vy). The initial velocity is determined by s and θ:

  1. vx = s cos θ
  2. vy = s sin θ

The physics simulation will proceed in small time steps Δt.3 At each time step, we will use the acceleration to update (vx, vy), and then use (vx, vy) to update (x, y). When y < 0, the ball has struck the earth and the simulation stops. In the update equations, I’ll use a prime (′) to mark the new, or updated, value of a variable. For example, x′ is the new value of x, and x is the old value of x.

The prime mark (′) is not easily visible!

  1. vx′ = vx if y ≥ 0, otherwise 0
  2. vy′ = vy + ay Δt if y ≥ 0, otherwise 0
  3. x′ = x + vx Δt
  4. y′ = y + vy Δt

Earth to Canvas

We’ve also got to transform earth coordinates to canvas coordinates. Remember, (0, 0) is the bottom left corner of the earth (yes, flat earth here, with an edge to fall off of just like in the old days!), but it’s the top right corner of the canvas. Similarly, on earth, a gun elevation of 45 degrees (π/4 radians) is halfway between right and up (1:30 o’clock); but on the canvas, it’s halfway between right and down (4:30 o’clock).

Let xc, yc, θc be the canvas-adjusted values for x, y, θ. The adjustments are:

  1. xc = x
  2. yc = canvas height − y
  3. θc = −θ

Okay, so in fact, we could skip these adjustments, if we don’t mind standing on our heads to watch the animation!

Scripting the Solution

We’ll do this two ways: first, without a cannon ball object; second, with a cannon ball object. The point is to emphasize the difference between using an object and not using an object.

First Solution: Without a Cannon Ball Object

Our canvas is set up in HTML:

<canvas id="canvas1" width="800" height="600">
  <p>A canvas should appear here.
    Your browser does not support the canvas element.
  </p>
</canvas>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.html}

As usual, we begin with a function definition,
and nearly everything interesting is hidden within the
closure:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.javascript}
function start_animation () {

    // The canvas and its properties
    var canvas = document.getElementById('canvas1');
    var ctx = canvas.getContext('2d');
    var CW = canvas.width; // (a)
    var CH = canvas.height; // (b)

Notice that we’ve used two objects already: we’re calling methods of the document and canvas objects (which we’ve done before); and now we’re also accessing the width and height properties of the canvas object, at lines (a) and (b) — in all previous scripts, we have put in numeric literals here, 800 and 600. The advantage of accessing these properties, instead of using numeric literals, is that if we change the width and height attributes of the <canvas> element, we no longer have to change the numbers in our script to keep it consistent.

We continue by declaring and initializing variables to keep track of the cannon ball:

    // Cannonball properties
    var init_speed = 89; // initial speed, meters/second
    var theta = Math.PI / 4; // firing elevation, 45 degrees (1:30)
    var x = 0, y = 0; // position
    var vx = init_speed * Math.cos(theta), 
        vy = init_speed * Math.sin(theta); // velocity

    var ax = 0,
        ay = -9.8; // acceleration, meters/second/second

    var ball_radius = 10; // pixels
    var ball_color = "black"; // ball color

Then a few more variables, representing the background (sky) color and some simulation parameters. We decide to update physics 60 times per second and to render the animation at 20 frames per second:

    // Environment conditions
    var sky_color = "gray"; 

    // Simulation parameters
    var physics_dt = 1000 / 60; // physics time step, 1/60 second
    var render_dt = 1000 / 20; // animation time step, 1/20 second

Next, we declare a couple of interval timer ID variables. These variables will be initialized later and used to stop the physics and animation.

    // Interval timers, initialized later
    var physics_iid, render_iid;

The physics interval timer will call the physics update function. This function uses a time step dt of 1/60 second. It first updates the velocity component vy (vx does not change), then uses the updated velocity to calculate new position (x, y). Finally, it checks if the cannonball has gone out of bounds (off the canvas). It would be enough, really, to check whether y < 0; but for good measure, the function also checks if has gone beyond any of the four edges of the canvas. Remember, || is the logical or operator. If the cannon ball is out of bounds, the function clears both interval timers, ending both the physics simulation and animation.

    // physics update
    function physics() {
        var dt = 0.001 * physics_dt; // milliseconds to seconds
        // update velocity vy; vx does not change
        vy = vy + ay * dt;
        // update position
        x = x + vx * dt;
        y = y + vy * dt;
        // stop simulation?
        if (y < 0 || y > CH || x < 0 || x > CW) {
            clearInterval(physics_iid);
            clearInterval(render_iid);
        }
    };

The render interval timer will call the render function, which simply renders the sky (background) and the ball. Rendering the sky is simply drawing a big sky-colored rectangle that fills the whole canvas.

    // animation update
    function render () {
        render_sky();
        render_ball();
    };

    function render_sky () {
        ctx.fillStyle = sky_color;
        ctx.fillRect(0, 0, CW, CH);
    };

The render_ball function draws the ball as a circle. A circle is described as an arc of 360 degrees or 2 π radians. The functions xcc and ycc convert x and y from earth to canvas coordinates.

    function render_ball () {
        // draw the ball as a circle,
        // i.e., an arch of 360 degrees or 2 pi radians
        ctx.beginPath();
        ctx.arc(xcc(x), ycc(y), ball_radius, 0, 2 * Math.PI, false);
        ctx.closePath();
        ctx.fillStyle = ball_color;
        ctx.fill();
    }

    // x to canvas coordinate
    function xcc(x) { return x; }

    // y to canvas coordinate
    function ycc(y) { return CH - y; }

(As it turns out, we didn’t need to convert θ to canvas θ.)

All of our inner functions are now defined. What’s left — we’re still in the outer function, start_animation — is to call render to draw the scene at time 0, then set up the two interval timers to repeatedly call the physics and render functions.

    render();
    physics_iid = setInterval(physics, physics_dt);
    render_iid = setInterval(render, render_dt);
}

Finally — and now outside the start_animation function — we call our function to get going:

start_animation();

View the result here (and just reload/refresh the page to play it again):

Second Solution: With Objects

Now we will introduce a ball object to consolidate our eight variables that describe the state of the ball. The changes in this version of the script will be in three places: the variables describing the ball, the physics function, and the render function.

So, instead of eight variables, we have one ball object:

    var ball = {'x': 0, 'y': 0, // position,
                'vx': init_speed * Math.cos(theta), 
                'vy': init_speed * Math.sin(theta), // velocity
                'ax': 0, 'ay': -9.8, // acceleration, meters/sec/sec
                'radius': 10, // pixels
                'color': 'black'};

Next, in the physics function, we’d replace x by ball.x, replace y by ball.y, and so forth. But wait — that would require typing ball. so many times — in fact, 13 times! Why not introduce a local variable b to stand for ball? Then we can shorten the code by three letters (“all”) for every occurrence of “ball.” (13 × 3 = 39 characters shorter):

    // physics update
    function physics() {
        var dt = 0.001 * physics_dt; // milliseconds to seconds
        var b = ball; // short variable name
        // update velocity vy; vx does not change
        b.vy = b.vy + b.ay * dt;
        // update position
        b.x = b.x + b.vx * dt;
        b.y = b.y + b.vy * dt;
        // stop simulation?
        if (b.y < 0 || b.y > CH || b.x < 0 || b.x > CW) {
            clearInterval(physics_iid);
            clearInterval(render_iid);
        }
    };

(Actually, I added back almost 39 characters to declare b. But still it’s easier to read, don’t you think?)

Finally, render_ball makes similar changes, prefixing x, y, radius, and color with b., which stands for ball.:

    function render_ball () {
        // draw the ball as a circle,
        // i.e., an arch of 360 degrees or 2 pi radians
        var b = ball;
        ctx.beginPath();
        ctx.arc(xcc(b.x), ycc(b.y), b.radius, 0, 2 * Math.PI, false);
        ctx.closePath();
        ctx.fillStyle = b.color;
        ctx.fill();
    }

Okay, this one is going to behave exactly the same as the first version, but we have to prove that it works, right?

View the result here (and again, just reload/refresh the page to replay it):

I have to admit this second version of the script is a bit more obnoxious because of all that b. notation, which occurs 13 times in physics and four times in render_ball! If there is going to be just one ball, then we had better stick with the first version.

But if there are going to be more balls, then the object notation will prove its worth. Suppose we had six balls. We had 8 variables for the ball in version one, replaced by a single object in version 2. If we had 6 balls, we’d need 6 × 8 = 48 variables to keep track of their state! That would be insane!

We’re be much better off with 6 ball object variables.

In fact, we can do better than that, using an array of objects, which would be a single variable. That is precisely what we’ll do in the next lesson.

References


  1. You can simulate class membership for JavaScript objects, but it isn’t necessary, and I don’t think we’ll be doing it in this course. Certainly not much.↩︎

  2. Object literal notation is the basis of JSON, JavaScript Object Notation, a form of data representation which is accessible from JavaScript and many other programming languages.↩︎

  3. As those who have studied calculus know, an exact solution involves the limit as Δt approaches 0. We’ll content ourselves with using a small time step to get an approximate solution.↩︎