Design Pepper

A Drip of JavaScriptAbout

Basic Inheritance with Object.create

Originally published in A Drip of JavaScript.

A few issues back we looked at how to implement basic inheritance with constructors. In this issue, we'll look at how to do the same with the newer Object.create.

When using constructors for inheritance, we attach properties to the constructor's prototype property like so:

Here's a little refresher:

function SuperHuman (name, superPower) {
    this.name = name;
    this.superPower = superPower;
}

SuperHuman.prototype.usePower = function () {
    console.log(this.superPower + "!");
};

var banshee = new SuperHuman("Silver Banshee", "sonic wail");

// Outputs: "sonic wail!"
banshee.usePower();

The SuperHuman constructor contains our initialization logic, while SuperHuman.prototype contains the methods that are shared across all SuperHuman instances.

If we were to implement the same basic logic using Object.create, it would look a bit different:

var superHuman = {
    usePower: function () {
        console.log(this.superPower + "!");
    }
};

var banshee = Object.create(superHuman, {
    name: { value: "Silver Banshee" },
    superPower: { value: "sonic wail" }
});

// Outputs: "sonic wail!"
banshee.usePower();

In this case we first define the prototype object superHuman, and then we use Object.create to make a new object which inherits from superHuman. That second argument might look a little strange to you, but it's just a simple property descriptor object, like we use with Object.defineProperty to fine-tune an object's properties.

Now, what if we want to create a new type which inherits from superHuman while adding its own functionality? What would that look like?

var superHero = Object.create(superHuman, {
    allegiance: { value: "Good" },
    saveTheDay: {
        value: function () {
            console.log(this.name + " saved the day!");
        }
    }
});

var marvel = Object.create(superHero, {
    name: { value: "Captain Marvel" },
    superPower: { value: "magic" }
});

// Outputs: "Captain Marvel saved the day!"
marvel.saveTheDay();

So far so good. But does Captain Marvel have access to the superHuman prototype methods?

// Outputs: "magic!"
marvel.usePower();

Yes, she does!

Using Object.create makes setting up inheritance chains simple because any object can be used as a prototype. However, inheritance managed by Object.create can't be detected by instanceof. Instead you'll need to use the isPrototypeOf method, like so:

// Outputs: true
console.log(superHero.isPrototypeOf(marvel));

// Outputs: true
console.log(superHuman.isPrototypeOf(marvel));

Because both superHero and superHuman are part of marvel's prototype chain, their isPrototypeOf calls each return true.

As with other JavaScript features we've reviewed, Object.create is a feature of ECMAScript 5 and is not available in older browsers like IE8.

That's our brief introduction to using Object.create. Thanks for reading!

Joshua Clanton

Avoiding Problems with Decimal Math in JavaScript

Originally published in A Drip of JavaScript.

One of the more unintuitive aspects of JavaScript, particularly for new developers, is the fact that decimal math doesn't always work as you'd expect it to. Suppose that Worf has exactly $600.90 of 21st century American dollars to trade for a wine of excellent vintage.

var worfsMoney = 600.90;
var bloodWinePrice = 200.30;
var worfsTotal = bloodWinePrice * 3;

// Outputs: false
console.log(worfsMoney >= worfsTotal);

// Outputs: 600.9000000000001
console.log(worfsTotal);

As you can see, simply checking whether Worf has enough money fails because his total doesn't come to exactly $600.90. But why is that?

In JavaScript all numbers are IEEE 754 floating point numbers. Due to the binary nature of their encoding, some decimal numbers cannot be represented with perfect accuracy. This is analagous to how the fraction 1/3 cannot be accurately represented with a decimal number with a finite number of digits. Once you hit the limit of your storage you'll need to round the last digit up or down.

Your first thought might be to try rounding to the second decimal place. Unfortunately, JavaScript's built-in rounding functions only round to the nearest integer.

Your second thought might be to do something like this:

// Outputs: true
console.log(worfsMoney.toFixed(2) >= worfsTotal.toFixed(2));

But even though we get the correct result, using toFixed means that we are just comparing formatted strings rather than actual numbers. And what we really want is a way to get an accurate numeric representation of our numbers.

Fortunately there is an easy way to do that in JavaScript. Instead of representing our money's value as decimal numbers based on dollars, we can just use whole integers of cents.

var worfsMoney = 60090;
var bloodWinePrice = 20030;
var worfsTotal = bloodWinePrice * 3;

// Outputs: true
console.log(worfsMoney >= worfsTotal);

// Outputs: 60090
console.log(worfsTotal);

Because integers can be represented perfectly accurately, just shifting our perspective to treat cents as the basic unit of currency means that we can often avoid the problems of comparisons in floating point arithmetic.

Of course, this doesn't solve all problems. Division of integers and multiplication by decimals may still result in inexact values, but basic integer addition, subtraction, and multiplication will be accurate. (At least until you hit JavaScript's upper limit for numbers.)

Thanks for reading!

Josh Clanton

Preventing Object Extensions in JavaScript

Originally published in A Drip of JavaScript.

In the last two drips, we've discussed safeguarding your objects with Object.seal and Object.freeze. In this issue we look at our final object protection mechanism: Object.preventExtensions.

Suppose that you have an object recording the populations of the continents. You probably don't want new continents added willy-nilly.

var continentPopulations = {
    africa: 1100000000,
    antarctica: 5000,
    asia: 4300000000,
    australia: 36000000,
    europe: 739000000,
    northAmerica: 529000000,
    southAmerica: 387000000
};

Object.preventExtensions(continentPopulations);

continentPopulations.atlantis = 50000;

// Outputs: undefined
console.log(continentPopulations.atlantis);

As you can see, the effect of Object.preventExtensions is to prevent the adding of new properties to an object. If you are using strict mode it will throw an error, but in normal mode it will fail silently.

It is important to remember that Object.preventExtensions does not prevent manipulating the values of existing properties.

// The antarctic population drops during winter
continentPopulations.antarctica = 500;

// Outputs: 500
console.log(continentPopulations.antarctica);

Nor does it prevent deleting existing properties.

// In the year 2248 Antarctica was destroyed
// by spacefaring dinosaurs from another dimension
delete continentPopulations.antarctica;

// Outputs: undefined
console.log(continentPopulations.antarctica);

To detect whether an object will accept new properties, use the corresponding Object.isExtensible method like so:

// Outputs: false
console.log(Object.isExtensible(continentPopulations));

Object.preventExtensions and Object.isExtensible are both part of the ECMAScript 5 specification, so older browsers like IE8 don't support them. And due to the limitations of previous versions of JavaScript, these features cannot be polyfilled.

That's it for our series on JavaScript's built-in object protection methods.

Sealing JavaScript Objects with `Object.seal`

Originally published in A Drip of JavaScript.

In the last drip we talked about making an object completely immutable. But suppose you need something a little less than full immutability? Then you're in luck. Object.freeze has a little brother named Object.seal.

Let's walk through how it works.

var rectangle = {
    height: 5,
    width: 10
};

Object.seal(rectangle);

rectangle.depth = 15;

rectangle.width = 7;

// Outputs: {
//     height: 5,
//     width: 7
// }
console.log(rectangle);

As you can see, once the object is sealed, new properties can't be added, but existing properties can still be modified.

In addition to preventing the addition of new properties, a sealed object can't have properties removed via delete. For example:

delete rectangle.width;

// Outputs: {
//     height: 5,
//     width: 7
// }
console.log(rectangle);

Object.seal has one final effect. It makes all object properties non-configurable, preventing you from configuring them into a different state with Object.defineProperty and similar methods.

Object.defineProperty(rectangle, "height", {
    writable: false
});

rectangle.height = 22;

// Outputs: 22
console.log(rectangle.height);

In this example, despite attempting to configure the writability to false, the height property remains writable. (For a refresher on Object.defineProperty, see drip #30.)

Attempting to make any of these forbidden modifications to a sealed object will either fail silently or (in strict mode) throw an error.

Fortunately, we also have a method to detect whether an object is sealed.

// Outputs: true
console.log(Object.isSealed(rectangle));

Like Object.freeze, Object.seal is part of the ECMAScript 5 specification, which means it isn't available in older browsers like IE8 and below. If you need to support those browsers, then you'll need to either avoid Object.seal or use feature detection to use it only in the browsers that support it.

Immutable Objects with `Object.freeze`

Originally published in A Drip of JavaScript.

One of the more common techniques in JavaScript is the use of an object to hold configuration values. The object might be accessed as a global or passed around as an argument. For example:

var artist = {
    name: "Johnny Cash",
    latestAlbum: "American V"
};

function announce (artist) {
    if (artist.name == "Johnny Cash") {
        console.log("The Man in Black");
    } else {
        console.log(artist.name);
    }
}

// Outputs: "The Man in Black"
announce(artist);

// Outputs: {
//     name: "Johnny Cash",
//     latestAlbum: "American V"
// }
console.log(artist);

But in either sort of situation, there is a problem. Functions that have access to the configuration object can modify the object, whether intentionally or accidentally. Suppose that you had a coworker modify the announce function above to highlight Elvis rather than Cash, but they mistyped the comparison.

var artist = {
    name: "Johnny Cash",
    latestAlbum: "American V"
};

function announce (artist) {
    // Whoops! Assigning the name rather than testing equality!
    if (artist.name = "Elvis Presley") {
        console.log("The King");
    } else {
        console.log(artist.name);
    }
}

// Outputs: "The King"
announce(artist);

// Outputs: {
//     name: "Elvis Presley",
//     latestAlbum: "American V"
// }
console.log(artist);

Hmm. I'm pretty sure that Elvis didn't record American V.

Some of you may be thinking that this what something like JSHint is for, and you'd be right. But as part of a defence in depth strategy, it would be awfully nice to make sure that our configuration objects don't get changed around after they're created. Fortunately, JavaScript gives us a way to do exactly that.

var artist = {
    name: "Johnny Cash",
    latestAlbum: "American V"
};

Object.freeze(artist);

function announce (artist) {
    // Whoops! Assigning the name rather than testing equality!
    if (artist.name = "Elvis Presley") {
        console.log("The King");
    } else {
        console.log(artist.name);
    }
}

// Outputs: "The King"
announce(artist);

// Outputs: {
//     name: "Johnny Cash",
//     latestAlbum: "American V"
// }
console.log(artist);

The Object.freeze method takes an object and renders it effectively immutable. Its existing properties may not be modified and new properties may not be added. In the example above this means that even though the logical error is still there, our artist object remains safe from modification and available for later use.

While in normal mode attempts to modify the object will fail silently, if you use strict mode, an error will be thrown.

var artist = {
    name: "Johnny Cash",
    latestAlbum: "American V"
};

Object.freeze(artist);

(function() {
    "use strict";

    // TypeError: Can't add property firstAlbum, object is not extensible
    artist.firstAlbum = "A Hard Days Night";
})();

Of course, we don't want to throw errors all over the place, so JavaScript also gives us a method to detect whether an object is frozen. Appropriately, it is named Object.isFrozen.

var artist = {
    name: "Johnny Cash",
    latestAlbum: "American V"
};

Object.freeze(artist);

// Outputs: "Frozen Artist!"
if (Object.isFrozen(artist)) {
    console.log("Frozen artist!");
}

Object.freeze is part of the ECMAScript 5 specification, which means it isn't available in older browsers like IE8 and below. If you need to support those browsers, then you'll need to either avoid it or use feature detection to ensure you use it only in supporting browsers.

I hope this quick introduction to Object.freeze helps you write more robust code.