Javascript trick shots
Javascript trick shots

Nov 2016

As with any programming language, there are many ways to the same end when using JavaScript. However there are a few tricks and shortcuts that can lead to neater code and less typing. In this post I'll go through some of my favourites along with a few of the advanced features of the language. Let's get into it...

Javascript trick shots

Ternary operators

Not exclusive to JavaScript by any means, the ternary operator (also refered to as the conditional operator) is a neat way to replace simple if ... else statements.

const doIt = false;
let result;
if(doIt){
    result = "Yes, I did it!";
}else{
    result = "No, I didn't";
}

Can be replaced with:

const doIt = false;
let result = doIt ? "Yes, I did it!" : "No, I didn't";

If the bit before the question mark is truthy, then the expression evaluates to the first statement, if not then it evaluates to the second statement.

Short-circuited OR expressions

When you write a logical statement in JavaScript, if you use boolean operands (true, false) then the statement will always return a boolean. However, if you're not using a boolean operands (e.g. {a:1, b:2}, [1,2,3,{...}] then the value that returned the outcome gets returned. This means that you can replace:

let defaultObj = {a:1, b:2};
if(!myObject){
    myObject = defaultObj;
}

with this:

let defaultObj = {a:1, b:2};
myObject = myObject || defaultObj;

If myObject is truthy then it gets assigned to itself. If it's falsy then assign defaultObj to it.

call, apply and bind

Javascript allows you to specify what this is bound to no matter how or where a function is called.

The call method allows you to call a function with a specific value of this. It allows you to call a function by providing it an object to bind this to. The first argument is what you want this to be and the remaing arguments become the arguments to the function you are calling:

"use strict";
const fastCar = {make:"Ferrari"};
const slowCar = {make:"Skoda"};

function car(){
    return `This car is a ${this.make}`;
}

car(); // Cannot read property 'make' of undefined
car.call(fastCar); // This car is a Ferrari
car.call(slowCar); // This car is a Skoda

function coloredCar(color){
      return `This ${color} car is a ${this.make}`;
}

coloredCar.call(fastCar, "red"); // This red car is a Ferrari

function updateCar(color, topSpeed){
      this.color = color;
      this.topSpeed = topSpeed;
 }

updateCar.call(fastCar, "Black", 210); // fastCar is now: { make: 'Ferrari', color: 'Black', topSpeed: 210 }
updateCar.apply(slowCar, ["Yellow", 95]); // slowCar is now: { make: 'Skoda', color: 'Yellow', topSpeed: 95 }

const updateFastCar = updateCar.bind(fastCar);

// the updateCar function has already been bound to fastCar. What happens if we call it on slowCar?
updateFastCar.call(slowCar, "Green", 50);

console.log(slowCar); // { make: 'Skoda', color: 'Yellow', topSpeed: 95 } Nothing happened to the Skoda. It's the same as before
console.log(fastCar); // { make: 'Ferrari', color: 'Green', topSpeed: 50 } This, however, updated. All because of the .bind method

// you can also bind arguments
const updateReallyFastCar = updateCar.bind(fastCar, "Midnight blue");
updateReallyFastCar.call(slowCar, 10);
console.log(slowCar); // { make: 'Skoda', color: 'Yellow', topSpeed: 95 }
console.log(fastCar); // { make: 'Ferrari', color: 'Midnight blue', topSpeed: 10 }

Array operations

There are already some neat array operations baked-in to JavaScript, which I'll discuss here. If you want more functionality for manipulating data then check out lodash for general manipulation and crossfilter for powerful map/reduce functionality.

Find

I'm sure you know about this one already, for some reason I always forget it's there andend up calling .indexOf, so that I can go and pull the correct element out of an array, like some sort of mug. .find willreturn you an object or null if it doesn't exist:

const arr = [{id:1, product: 'Macbook Pro'}, {id:2, product: 'Macbook Air'}];
arr.find(o=> o.id === 1); // returns object {id:1, product: 'Macbook Pro'}

Note that you can also pass in the index of the element to your function. So to ignore anything before the 10th element you would write myArray.find((x,i)=> i>=10)]

Filter

This is a seriously handy function and I use it daily. The naive approach would be to loop through your array and pull out the items you want, storing them in another array. However the.filter function makes light work of this common task:

const arr = [{id:1, product: 'Macbook Pro'}, {id:2, product: 'Macbook Air'}, {id:3, product: 'iPhone 7'}];
arr.filter(o=> o.id > 1); // returns  [{id:2, product: 'Macbook Air'}, {id:3, product: 'iPhone 7'}]

Map

Another common scenario is wanting to take an array of objects and transform (map) them to something different.So if we just wanted a list of products, from the arr array in the previous example, we could map that field like so:

arr.map(o=> o.product); // returns  ['Macbook Pro', 'Macbook Air', 'iPhone 7']

Reduce

If this was a weapon in Call of Duty it would be regarded as overpowered. reduce is capable of doing a map, a filter, or pretty much anyarray manipulation that you can think of. With most other array callback, the first element passed into the callback is the current array element. With reduce, however,the first value is an accumulator, which is what the array is being reduced to. The rest of the arguments are what you'd expect: the current element, index, and the array itself.

const nums = [1,2,3,4,5];
const sum = nums.reduce((a,x)=> a+=x, 0);

Here we see that the function passed into reduce takes 2 params: the accumulator and the current array element. Also note that we initialise the accumulator as zero.

Finally, here's a more involved example. Donald Knuth's variance algorithm:

const data = [1,2,3,4,5,10,20,30];
const stats = data.reduce((a,x)=>{
    a.N++;
    let delta = x = a.mean;
    a.mean += delta/a.N;
    a.M2 += delta*(x-a.mean);
    return a;
}, {N:0, mean:0, M2:0});

if(stats.N>2){
    stats.variance = stats.M2/(stats.N-1);
    stats.stdev = Math.sqrt(stats.variance);
}
Please enable JavaScript to view the comments powered by Disqus.