Design Pepper

A Drip of JavaScriptAbout

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!");
}

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

Retrieving Property Names with `Object.getOwnPropertyNames` and `Object.keys`

Originally published in A Drip of JavaScript.

In past versions of JavaScript it was fairly painful to figure out what properties an object possessed. Essentially you would need to manually iterate over the object and filter out inherited properties, like so:

var charactersBooks = {
    Frodo: "Lord of the Rings",
    Aslan: "Chronicles of Narnia",
};

var characters = [ ];

for (var prop in charactersBooks) {
    if (charactersBooks.hasOwnProperty(prop)) {
        characters.push(prop);
    }
}

// Outputs: ["Frodo", "Aslan"]
console.log(characters);

But with ECMAScript 5 we get access to Object.keys which eliminates this tedious boilerplate.

var charactersBooks = {
    Frodo: "Lord of the Rings",
    Aslan: "Chronicles of Narnia",
};

var characters = Object.keys(charactersBooks);

// Outputs: ["Frodo", "Aslan"]
console.log(characters);

As might be expected, Object.keys only retrieves the names of properties that are declared directly on the object. It doesn't get the names of inherited properties. In addition, it only retrieves the names of enumerable properties. Non-enumerable property names are omitted.

But ES5 also gave us another method called Object.getOwnPropertyNames which is less strict about which property names it will retrieve. Like keys, getOwnPropertyNames will only retrieve "own" properties. However, it will retrieve the names of non-enumerable properties.

Not sure what enumerable means in this context? Non-enumerable properties are essentially properties that shouldn't be "counted" or iterated over for some reason. (More discussion to come in future drips.) The simplest example is probably an array's length property. Let's see how our functions treat it.

var authors = ["Tolkien", "Lewis"];

// Outputs: ["0", "1"]
console.log(Object.keys(authors));

// Outputs: ["0", "1", "length"]
console.log(Object.getOwnPropertyNames(authors));

As you can see, Object.keys skipped over the non-enumerable length property while Object.getOwnPropertyNames did not.

How do you know when to use one or the other? My personal rule is to always default to using Object.keys unless I specifically need the name of non-enumerable properties.

Because both of these functions are part of the ES5 spec, they are not available in older browsers like IE8. And unfortunately, it is impossible to backport Object.getOwnPropertyNames. However, Object.keys can be backported with something like the ES5 Shim library.

The `delete` Operator in JavaScript

Originally published in A Drip of JavaScript.

The delete operator is one of the less frequently used aspects of the JavaScript language. But there are times when you need delete and nothing else will do. In this drip, we'll dive into how to use it and how it works.

The purpose of delete, as you might imagine, is to delete things. More specifically, it will delete object properties. For example:

var multiverse = {
    earth1: "Silver Age",
    earth2: "Golden Age"
};

delete multiverse.earth2;

// Outputs: { earth1: "Silver Age" }
console.log(multiverse);

The delete operator will not delete ordinary variables.

var alex = "Alexander Luthor";

delete alex;

// Outputs: "Alexander Luthor"
console.log(alex);

However, it will delete "global variables," since they are actually properties of the global object (window in the browser).

// Because var isn't used, this is a property of window
classicFlash = "Jay Garrick";

delete window.classicFlash;

// ReferenceError: classicFlash is not defined
console.log(classicFlash);

The delete operator also has a return value. If it succeeds in deleting a property, it will return true. If it fails to delete a property because the property is unwritable it will return false, or if in strict mode it will throw an error.

var multiverse = {
    earth1: "Silver Age",
    earth2: "Golden Age"
};

var earth2Deleted = delete multiverse.earth2;

// Outputs: true
console.log(earth2Deleted);

You are probably wondering under what circumstance you'd want to use delete. The answer is whenever you actually want to remove a property from an object.

Sometimes rather than delete a property, JavaScript developers will just give it a value of null, like so:

var multiverse = {
    earth1: "Silver Age",
    earth2: "Golden Age"
};

multiverse.earth2 = null;

While this effectively severs the property from the original value, the property itself still exists on the object, as you can see below:

// Outputs: {
//    earth1: "Silver Age",
//    earth2: null
// }
console.log(multiverse.earth2)

And some operators like in and the for in loop will still report the presence of the null property. If you are passing around an object that might be inspected using those methods, you probably want to make sure that you really delete any unwanted properties.

Finally, you should keep in mind that delete doesn't actually destroy the property's value, just the property itself. For example:

var earth3 = "The Crime Syndicate";
multiverse.earth3 = earth3;

delete multiverse.earth3;

// Outputs: "The Crime Syndicate";
console.log(earth3);

Here, both earth3 and multiverse.earth3 referred to the same value. And as you can see, deleting multiverse.earth3 doesn't affect earth3 at all.

That's it for our overview of delete.

Basic Inheritance with JavaScript Constructors

Originally published in A Drip of JavaScript.

We've looked before at using JavaScript's constructors to create our own custom object types. But what we didn't look at was how we can create an inheritance hierarchy.

It's important to note that even though constructors are often referred to as "classes," they really aren't the same thing as classes in other languages. In JavaScript, a constructor is just a function invoked by the new operator which builds a new object.

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.

But suppose that we want to create a new type which inherits from SuperHuman while adding its own functionality? What would that look like?

function SuperHero (name, superPower) {
    this.name = name;
    this.superPower = superPower;
    this.allegiance = "Good";
}

SuperHero.prototype.saveTheDay = function () {
    console.log(this.name + " saved the day!");
};

var marvel = new SuperHero("Captain Marvel", "magic");

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

While this gets us started, there are a couple of problems. First of all, the SuperHero constructor is repeating some of the logic of the SuperHuman constructor. And more importantly, at this point instances of SuperHero don't have access to SuperHuman methods. For example:

// TypeError: Object <#SuperHero> has no method 'usePower'
marvel.usePower();

Let's fix those couple of issues.

function SuperHero (name, superPower) {
    // Reuse SuperHuman initialization
    SuperHuman.call(this, name, superPower);

    this.allegiance = "Good";
}

SuperHero.prototype = new SuperHuman();

SuperHero.prototype.saveTheDay = function () {
    console.log(this.name + " saved the day!");
};

var marvel = new SuperHero("Captain Marvel", "magic");

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

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

We've managed to eliminate the repeated constructor logic by calling SuperHuman with SuperHero's this object and passing along the necessary arguments. That ensures that SuperHuman's initialization logic will act on the new SuperHero object. And then we tack on the additional logic that is specific to SuperHero.

But where the inheritance comes in is on SuperHero.prototype. In order to ensure that it inherits the methods from SuperHuman.prototype, we actually make it an instance of SuperHuman with new SuperHuman().

This basic inheritance pattern won't always work, particularly if the parent constructor is complex, but it will handle simple situations quite well.

In future issues we'll take a look at more sophisticated ways of doing inheritance.

JavaScript's Primitive Wrapper Objects

Originally published in A Drip of JavaScript.

We've talked before about how in JavaScript most things behave like objects even when they aren't objects. For instance, consider how we can call methods on a string even though it is a primitive:

// Outputs: "FRED FLINTSTONE"
console.log("Fred Flintstone".toUpperCase());

How does that work, though? Initially you might think that strings are really objects in disguise and try assigning properties to them.

var fred = "Fred Flintstone";
fred.favoriteFood = "Brontosaurus Steak";

// Outputs: undefined
console.log(fred.favoriteFood);

But that doesn't work. And even more strangely, it doesn't trigger an error. It turns out that in order to allow you to call methods on a primitive, JavaScript does a little bit of trickery which we'll get to shortly.

Apart from null and undefined, each primitive type (string, number and boolean) has a corresponding object equivalent which you can create by invoking its constructor. For instance:

var barney = new String("Barney Rubble");

// Outputs: "Barney Rubble"
console.log(barney);

barney.favoriteFood = "Pterodactyl Eggs";

// Outputs: "Pterodactyl Eggs"
console.log(barney.favoriteFood);

// Outputs: "object"
console.log(typeof barney);

As you can see, though, the string object can have properties assigned to it, and it reports itself to be of type "object."

The trickery I mentioned before is that any time you attempt to access a property on a primitive, JavaScript will implicitly create a temporary wrapper object. We can verify this by doing the following:

String.prototype.reportType = function () {
    return typeof this;
};

var fred = "Fred Flintstone";

// Outputs: "string"
console.log(typeof fred);

// Outputs: "object"
console.log(fred.reportType());

When we directly check the type of a string primitive we get "string" as expected, but when we check the type of this in a method executed on a string primitive we get "object".

The JavaScript engine doesn't keep this wrapper object around, though. As soon as the work of the method (or other property) is done, it is disposed of.

This explains why trying to assign properties to a primitive doesn't work, but also doesn't throw an error. Assigning the property succeeds, but the property is set on a wrapper object which is immediately destroyed. So when you go to look up the property later, there is nothing there anymore.

I hope this has helped you better learn the quirks of JavaScript primitives.