JS Wickedness

We are all familiar with the famous WAT talk (and if you're not, go watch that now), in which Gary Bernhardt pointed out both his mastery of memes and some of the unique ways in which JavaScript handles data. There are a few other bits of JS "creativity" that are worth pointing out for those that don't know them. They aren't puzzling the way the WAT talk is, instead they are testaments of how awesome JavaScript can be, when you deeply explore what it is capable of doing.

The comma operator (,)

We all know and love the comma as the separator between array elements, and the separator between function parameters and arguments. However, most of us don't realize that the comma operator is an operator of its very own.

The comma can be used to separate operations, but it is only ever the last one in a sequence that is returned as the result of the collection of actions. That was a confusing sentence that only gets clearer when I demonstrate the concept with some code. Take the following code as an example:

function iMakeToots() {
  return "Beep", "Boop", "Floop", "Toot";
}

console.log(iMakeToots()); //"Toot"

If you ran this code, or just took my comment as fact, then you can see that the string "Toot" is what was returned from the function. All of the other operations (separated by commas) were done, in that each of those strings was created in memory, but the last one was the only action that was given to the return keyword. So, that is why it was returned from the function.

Here is a slightly more elaborate example:

function iMakeBoops() {
  console.log("Boop");
}

function iMakeToots() {
  return "Beep", iMakeBoops(), "Floop", "Toot";
}


console.log(iMakeToots()); 
//"Boop" here, then a...
//"Toot"

This demonstrates the evaluation aspect of this operator. The function call was evaluated, even though only "Toot" was returned from the function.

The or operator (||)

Most of us, when we start programming, use the || operator in our if statements. What we don't usually think of is using this operator in our variable assignments. It's a great way to provide a default value for something. Take the following example:

function User(props) { 
  this.name = props.name;
  this.email = props.email;
  this.admin = props.isAdmin || false;
}

let donProps = {
  name: "Don Burks",
  email: "don@donburks.com",
  isAdmin: false
};

let don = new User(donProps);

console.log(don.admin); //false

Using this method, we have defaulted the admin value for the user, so that no matter what is passed in for the isAdmin property, we can guarantee that if that value does not evaluate to a truthy value, it will be false. If the prop isn't even passed (and therefore is undefined), it would get a valid value of false. This is different from providing default values for function parameters, though. Don't get confused on that point.

But we can go one level deeper down the rabbit hole with the || operator, and use it to even decide what function to run. Consider the following code:

function noop() {}

function speak(num) {
    console.log(`I say ${num} things.`);
}

function yell(num) {
    console.log(`I _YELL_ ${num} things.`);
}

let actions = [speak, null, undefined, yell, false, yell, yell, speak, 0, yell];
let rand = Math.floor(Math.random() * actions.length);
let action = actions[rand];

(action || noop)(3);

Here is a very different implementation of the || operator than we normally see. It is also doing exactly the same thing as we did before with the default value. The || operator is looking at the first value (the value on the left) and evaluating it to see if it is a truthy value. If it is, that is returned from the expression, otherwise the value from the right is used.

Also in the syntax, the first set of parentheses is used to group that expression together, and the result of that expression will be returned before further evaluation takes place. Once that first set of parentheses have concluded, then the second set will be evaluated. As we know, anytime parentheses are appended to a value in JavaScript, it will be evaluated as a function. With this in mind, there are now only three values that can be returned from the first set of parentheses:

As all three of these have functions defined, then whichever one that is used will result in a function being invoked.

Spread operator (...)

The spread operator has been one of the relatively unsung heroes of the ES6 and beyond additions to the JavaScript toolkit. Admittedly, many articles have been written about it, but while most people jumped on let, const, and fat-arrow functions, the spread operator is often that "next level" of comfort with the newer tools that developers have to work to embrace.

However useful it may be for merging arrays, merging objects, and passing arguments to a function, there is another use for it that you might not expect. You can use the spread operator to build a range ability into JavaScript. Many other languages have this, and libraries like lodash and others have included this functionality.

At its basic level, I should be able to run a function like range(x) and have it return to me an array of numbers from 1 to x, inclusive. Example:

range(5); //[1, 2, 3, 4, 5]

However, JS doesn't have this ability natively, and as developers whenever we need to generate this, we resort to some kludge or hack that gives it to us. The simplest way I have found so far is by taking advantage of the spread operator. Here is an implementation of the range() function, using spread:

function range(num) {
    return [...Array(num + 1).keys()].slice(1);
}

range(10); //[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Step-by-step, this function:

Any other features of a range() function, such as reverse ordering, stepping, and other abilities would have to be added in from here, but as a base this is a good start.

Summary

I hope you've enjoyed this exploration of a few of the features of JavaScript and what it's capable of doing. What are other unexpected ways in which you find yourself using "standard" parts of the language? Shoot me an e-mail and let me know!


If you have any comments or questions about this post, please feel free to shoot me an e-mail at don (at) donburks (dot) com. I would love to hear from you and continue the conversation.