Design Pepper

A Drip of JavaScriptAbout

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.

Ditching jQuery with `querySelectorAll`

Originally published in A Drip of JavaScript.

For many developers, jQuery serves as their first introduction to JavaScript. And jQuery does a great job of easing the learning curve by hiding away browser inconsistencies and using an intuitive chaining syntax. But probably the most distinctive feature of jQuery is its use of CSS selectors to choose which elements to manipulate.

For example, consider an HTML page that looks like this:

<!doctype html>
<html>
    <head>
        <title>Programming Languages</title>
    </head>
    <body>
        <h1>Programming Languages</h1>

        <ul>
            <li>JavaScript</li>
            <li>CoffeeScript</li>
            <li>Ruby</li>
            <li>Python</li>
        </ul>

        <script src="example.js"></script>
    </body>
</html>

If you want to use jQuery to add a class to the first and last li, that's as simple as:

$("li:first-child, li:last-child").addClass("list-end");

What many people don't realize is that modern web browsers actually support a native DOM method that can use CSS selectors in exactly the same way. Let's see how that works.

var selector = "li:first-child, li:last-child";
var listEnds = document.querySelectorAll(selector);

var listEndsArr = [].slice.call(listEnds);

listEndsArr.forEach(function (el) {
    el.className += "list-end";
});

As you can see, querySelectorAll will return a list of matching elements. However, the list that it returns is a NodeList, one of those "array-like" objects that isn't really an array. That means that we can't directly use some array methods like forEach.

To get around that problem, we use slice.call to create a real array of the matching elements. And once we have a real array we use forEach to add the class to each matching element.

There are some issues to keep in mind when using querySelectorAll. First, the CSS selectors you can use will be limited by the selectors that the browser supports. Second, while it enjoys good support among modern browsers, querySelectorAll is not available in IE7 and below.

Of course, querySelectorAll isn't a full replacement for jQuery, but it does get you one step closer to jQuery independence.