Design Pepper

A Drip of JavaScriptAbout

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.

Storing Metadata on Arrays in JavaScript

Originally published in A Drip of JavaScript.

We've talked before about the fact that in JavaScript, even arrays are objects. But one thing we haven't really talked about is the sort of flexibility that this implies.

Suppose that we have a system which we can query for records, but we want to be able to capture the time at which those records were returned. We could do something like this:

var digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

function filterDigits (filterFn) {
    return {
        result: digits.filter(filterFn),
        timestamp: new Date()
    };
}

var filterObj = filterDigits(function(x) {
    return (x > 8);
});

// Outputs: [9]
console.log(filterObj.result);

// Outputs: Mon Nov 04 2013 13:34:09 GMT-0500 (EST)
console.log(filterObj.timestamp);

This works okay. But if you think about it, it is a little odd having to create an entirely new object just to store metadata. Wouldn't it be nice if you could just store that metadata directly on the the resulting array? It turns out that you can.

var digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

function filterDigits (filterFn) {
    var result = digits.filter(filterFn);
    result.timestamp = new Date();

    return result;
}

var filtered = filterDigits(function(x) {
    return (x > 8);
});

// Outputs: [9]
// Visual output may vary depending on your console.
console.log(filtered);

// Outputs: Mon Nov 04 2013 13:34:09 GMT-0500 (EST)
console.log(filtered.timestamp);

Because an array is an object, you can assign arbitrary properties to it. Using this approach means that our code maintains focus on the central concern (the results array) while still carrying around the metadata for those places which need it.

Looking at the changes in the variable names can help illuminate why it is an improvement. On the one hand, the name filterObj essentially means "a collection of disparate values." The name filtered on the other hand, conveys a single concept.

It's a little improvement, but little improvements add up over time.