Categories
JavaScript Best Practices

Better JavaScript — Higher-Order Functions and Call

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at ways to improve our JavaScript code.

Things we can do with Higher-Order Functions

We can do various things with higher-order functions.

They let us simplify the code that we repeat.

For instance, instead of writing a loop to loop through an array and push the entries to another array.

Instead of writing:

const names = ["foo", "bar", 'baz'];
const upper = [];
for (let i = 0; i <  names.length; i++) {
  upper[i] = names[i].toUpperCase();
}

We write use the map method to do the same thing:

const names = ["foo", "bar", 'baz'];
const upper = names.map((name) => name.toUpperCase());

We called map with a callback to return each entry as upper case.

It’s much shorter and simpler.

We can make our own higher-order function that takes a callback and do something by taking a callback function to make it do what we want.

For instance, we can write:

const buildString = (n, callback) => {
  let result = "";
  for (let i = 0; i < n; i++) {
    result += callback(Math.floor(Math.random() * 100) + i);
  }
  return result;
}

const str = buildString(8, String.fromCharCode);

We have created the buildString function that takes a callback function.

In the function, we created a for loop and call the callback with each iteration of the loop.

callback is is called with the character code to let us get the character.

And then we can call it by using passing in a number for the length and String.fromCharCode .

Then str is 'BO@B1&g’ .

Higher-order functions let us fix any bugs with just once since there’s no repeated code.

And there’s no code spread everywhere in our program.

Use call to Call Methods with a Custom Receiver

We can use the call method on a function to let us set the value of this and arguments we call with a function.

For instance, if we have:

f.call(obj, arg1, arg2, arg3);

Then that’s like calling:

f.call(arg1, arg2, arg3);

and setting this in f to obj .

The call method comes in handy for calling methods that may have been removed, modified or overridden.

For instance, the hasOwnProperty can be overridden by any object.

We can set:

obj.hasOwnProperty = 1;

Then if we write:

obj.hasOwnProperty("foo");

and we’ll get an error since hasOwnProperty has been set to 1.

The better to call hasOwnProperty is to use the call method.

For instance, we can write:

const dict = {};
dict.foo = 1;
delete dict.hasOwnProperty;
({}).hasOwnProperty.call(dict, "foo");

We created the dict object with the foo property.

And we deleted the hasOwnProperty property from dict .

Even if we did that, we can still call hasOwnProperty with an empty object by using the call method.

The first argument is the value of this in that we want to call hasOwnProperty with.

And the argument is the argument for hasOwnProperty .

Conclusion

Higher-order functions let us abstract out the logic into reusable pieces.

The call method lets us call a function with whatever value of this we want.

Categories
JavaScript Best Practices

Better JavaScript — Function Declarations in Blocks

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at ways to improve our JavaScript code.

Unportable Scoping of Block Function Declarations

We shouldn’t have function declarations inside a block.

This isn’t valid JavaScript but it’s still allowed by most JavaScript engines.

This is because we have problems with the results that it produces.

If we have:

function foo(x) {
  function f() {
    return "bar";
  }
  var result = [];
  if (x) {
    result.push(f());
  }

  result.push(f());
  return result;
}
console.log(foo(true));
console.log(foo(false));

then we get:

["bar", "bar"]
["bar"]

like we expect since we pushed f ‘s returned result to the result array.

But when we have something like:

function f() {
  return "global";
}

function foo(x) {
  var result = [];
  if (x) {
    function f() {
      return "bar";
    }
    result.push(f());
  }
  result.push(f());
  return result;
}
console.log(foo(true));
console.log(foo(false));

Then the 2nd foo call won’t run since f is assumed to be the one inside.

This is something that would trip many people up.

The code function will still be allowed by JavaScript engines, but we get weird results like this.

Functions declarations shouldn’t be inside another block or function, so we should avoid this.

What we should do is either keep the inner function outer, or we can assign it to a local variable.

For instance, we can write:

function f() {
  return "global";
}

function test(x) {
  var result = [];
  if (x) {
    const g = f;
    result.push(g());
  }
  result.push(f());
  return result;
}

Then there won’t be any issues with scoping.

Don’t Create Local Variable with eval

eval should definitely never be used because of its security and performance issues.

Another way that eval is bad is creating local variables with it.

For instance, if we have:

function foo(x) {
  eval("var y = x;");
  return y;
}

console.log(foo("bar"));

Then we see 'bar' logged.

We created the variable y by assigning parameter x to it in the string that we passed into eval .

The problem arises if we make the eval call conditional.

For instance, if we have:

var y = "global";

function foo(x) {
  if (x) {
    eval("var y = 'local';");
  }
  return y;
}
console.log(foo(true));
console.log(foo(false));

then the scoping is done according to the condition that we have in foo .

This is definitely a problem since we’ve to go through the code and the code is a string.

Another way that’s bad is that we have:

var y = "global";

function bar(src) {
  eval(src);
  return y;
}
console.log(bar("var y = 'bar';"));
console.log(bar("var z = 'bar';"));

A string is passed into the bar fucntion and we run eval with it.

This pollutes the bar function’s scope with extra variables defined from the outside.

This is even more confusing.

These are more reasons we shouldn’t use eval for any purpose.

Conclusion

We shouldn’t have function declarations within another block.

It’s not valid JavaScript but it’s still allowed.

Also, we shouldn’t use eval in any situation.

Categories
JavaScript Best Practices

Better JavaScript — Function Calls

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at ways to improve our JavaScript code.

Call Functions with Different Numbers of Arguments with apply

The apply method lets us call a function with a value of this and an array with the arguments.

For instance, we can write:

const min = Math.min.apply(null, [1, 2, 3]);

The first argument is the value of this , which can be anything since Math.min is a static method.

And the rest are the arguments.

This lets us call a function with a variable number of arguments.

We can also use it by getting the values from within the function.

For instance, we can write:

const buffer = {
  state: [],
  append(...args) {
    for (const a of args) {
      this.state.push(a);
    }
  }
};

We get the args which have the arguments.

Then we push the items into the this.state array.

Then we can use it by writing:

buffer.append.apply(buffer, [1, 2, 3]);

Use the Rest Operator to Create Variadic Functions

We can use the rest operator to get all the arguments of a function.

For instance, we can write:

function average(...args) {
  let sum = 0;
  for (const a of args) {
    sum += a;
  }
  return sum / args.length;
}

We get the array of arguments from the args array generated from the rest operator.

Then we can loop through the items and get the sum.

And then we divide it by the length of the args array.

This is better than using the arguments objects to get arguments.

Don’t Use arguments to Create Variadic Functions

The rest operator replaces the arguments object to get a variable number of arguments.

So we shouldn’t write:

function average() {
  let sum = 0;
  for (const a of arguments) {
    sum += a;
  }
  return sum / args.length;
}

Instead, we should use the rest operator instead.

It also only works with traditional functions and not arrow functions so its use is limited.

Never Modify the arguments Object

The arguments object looks like an array, but it’s not an array.

It doesn’t have any array methods.

However, with the call method, we can call array methods like:

const shift = [].shift;
shift.call(arguments);

But if we have:

const obj = {
  add(x, y) {
    return x + y;
  }
};

function call(obj, method) {
  const shift = [].shift;
  shift.call(arguments);
  shift.call(arguments);
  return obj[method].apply(obj, arguments);
}

call(obj, 'add', 1, 2, 3)

That would get us the error ‘Uncaught TypeError: Cannot read property ‘apply’ of undefined’.

This means we can’t change the arguements object to call an object with arguments.

Use bind to Curry Functions

The bind method lets us return a function with a value of this inside it.

It can also be used to return a new function with some arguments of the existing function applied.

For example, if we have the following function:

function add(x, y) {
  return x + y;
}

Then we can use bind to create a function with the first argument applied by writing:

const add2 = add.bind(null, 2);

the first argument is the value of this .

And the subsequent arguments are the arguments for the function.

Then we can use it by writing:

const sum  = add2(3);

And sum would be 5.

Conclusion

apply and bind are useful for changing the this value of the function and applying arguments.

apply calls the function and bind returns a new function with the values applied.

Categories
JavaScript Best Practices

Better JavaScript — Floating-Point and Coercions

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at ways to improve our JavaScript code.

Floating Point Numbers

Floating-point numbers are tricky in JavaScrtipt because of rounding.

For instance, if we have:

0.1 + 0.2;

Then we get:

0.30000000000000004

Double-precision floating-point numbers can only represent a finite set of numbers, rather than an infinite set of real numbers.

This means that floating-point arithmetic can only produce finite results.

The rounding errors can accumulate when we round in one or more operations.

This makes the result less and less accurate.

Math operations like addition is associative, so (x + y) + z = x + (y + z).

But this isn’t the case with floating-point arithmetic.

So if we have:

(0.1 + 0.2) + 0.3; 

We get:

0.6000000000000001

But:

0.1 + (0.2 + 0.3);

returns 0.6.

With integers, we’ve to make sure that the range of each result is between -2 ** 53 and 2 ** 53 .

Implicit Coercions

We got to be aware of implicit coercions.

They’re going to be a problem for us when we write JavaScript.

For instance, we can write:

2 + true;

and get 3.

true is converted to 1 so we get 3.

There’re some cases that JavaScript gives us the error if we use the wrong type.

For instance, if we have:

"foo"(1);

We get ‘Uncaught TypeError: “foo” is not a function’.

And undefined.x gets us ‘Uncaught TypeError: Cannot read property ‘x’ of undefined’.

In many other cases, JavaScript coerces the value to what it thinks it should be.

So if we write:

2 + 1;

then JavaScritp assumes that we’re adding 2 numbers.

And if we write:

"foo" + " bar";

Then we’re concatenating 2 strings.

If we combine a number and a string, then it’s more confusing.

If we have:

1 + 2 + "3";

or

(1 + 2) + "3";

then we get '33' .

But if we have:

1 + "2" + 3;

We get “123” .

It just converts to the type according to its own rules, so it’s not very predictable.

Type coercion hides errors, so we only know that we have a bug if the code doesn’t do what we expect.

Rather than throwing an exception, we just see results we don’t expect.

Also, NaN is considered to be not equal to itself, which is more confusing.

We’ve to call isNaN to check for NaN .

So:

isNaN(NaN); 

returns true .

But:

let x = NaN;   
x === NaN;

returns false .

Silent coercions make debugging harder since it covers up errors and makes them harder to find.

When calculation goes wrong, we’ve to inspect each variable to check where something goes wrong.

Objects can also be coerced to primitives.

So if we have:

'' + Math

we get “[object Math]” .

And:

'' + JSON

returns “[object JSON]” .

Conclusion

Floating-point arithmetic and data type coercions are both tricky with JavaScript.

We got to make sure we coerce data types as little as possible to reduce the chance of frustrating bugs.

Categories
JavaScript Best Practices

Better JavaScript — Different Kinds of Functions

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at ways to improve our JavaScript code.

Know the Difference Between Function, Method, and Constructor Calls

We should understand the difference between function, methods, and constructors.

These are all functions but they serve different purposes.

Functions are some pieces of reusable code that we can call.

They might take inputs and return something.

For instance, a function is something like:

function hello(username) {
  return `hi ${username}`;
}

Then we can call it by writing:

hello('james');

Methods in JavaScript are functions that are in objects.

For instance, we can write:

const obj = {
  hello() {
    return `hi ${this.username}`;
  },
  username: 'jane smith'
};

We have the obj.hello method.

this is obj since the method is in the obj object.

Then we can call it by writing:

obj.hello();

and we get 'hi jane smith' returned.

We can define a function outside the object and then add it as a method.

For instance, we can write:

const hello = function() {
  return `hi ${this.username}`;
}

const obj = {
  hello,
  username: 'jane smith'
};

Then obj.hello() returns the same result.

We can reuse hello to define a method in another function.

For instance, we can write:

const hello = function() {
  return `hi ${this.username}`;
}

const obj = {
  hello,
  username: 'jane smith'
};

const obj2 = {
  hello,
  username: 'may wong'
};

Then when we call:

obj2.hello()

it returns ‘hi may wong’ .

A function with this isn’t useful outside an object.

For instance, if we have:

const hello = function() {
  return `hi ${this.username}`;
}

console.log(hello())

Then we get 'hi undefined' if we call hello directly since this is window if strict mode is off.

If we have strict mode on:

const hello = function() {
  'use strict';
  return `hi ${this.username}`;
}

then we get something different.

We get ‘Uncaught TypeError: Cannot read property ‘username’ of undefined’ since this is undefined at the top level with strict mode on.

Another use of JavaScript functions is constructors.

They let us create objects of a given type.

For instance, we can write;

function Person(name) {
  this.name = name;
}

Then we can create a new Person instance with the new operator.

For instance, we can write:

const james = new Person('james');

Then james.name is 'james' .

Higher-Order Functions

Higher-order functions are functions that take a function as a parameter or return a function.

For instance, the sort method of the array instance is a higher-order function.

We can use it by writing:

const sorted = [3, 1, 2, 5, 5, 9].sort((a, b) => a - b);

Then sorted is [1, 2, 3, 5, 5, 9] .

We passed in a callback to the sort method so that it’s used for sorting the numbers in the array.

a and b are the entries in the array being processed.

Conclusion

Different kinds of functions do different things.

Higher-order functions are functions that takes a function as a parameter or returns a function.