Categories
JavaScript Best Practices

Better JavaScript — Primitives

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.

Converting Objects to Primitives

Objects can be converted to numbers with the valueOf method.

For instance, we can write:

const obj = {
  valueOf() {
    return 3;
  }
};

console.log(2 + obj);

then we see 5 logged.

We can so convert an object to a string with:

const obj = {
  toString() {
    return 'bar';
  }
};

console.log('foo' + obj);

Then we get:

'foobar'

logged.

If an object has both the valueOf and toString methods, then it’s not obvious which one will be run.

So if we have:

const obj = {
  toString() {
    return "[object obj]";
  },
  valueOf() {
    return 'bar';
  }
};

console.log('foo' + obj);

Then we see 'foobar' logged.

So we know that valueOf will be run if both valueOf and toString are both present.

valueOf is designed to be used for objects that represents numeric values like Number objects.

For these objects, toString and valueOf are consistent.

They both return the string representation of a number.

Coercion to strings are far more common than coercion to numbers.

It’s best to avoid valueOf unless the object is an abstraction of numbers.

Another kind of coercion is known as truthiness.

The || and && work with boolean values, but they can accept anything.

If a value is truthy, then they’re coerced to true .

Otherwise, they’re falsy and they’re coerced to false .

false, 0, -0, “”, NaN, null, and undefined are falsy.

Other values are all truthy.

The || operator lets us return a default value if the first operand is falsy.

For instance, instead of writing:

function point(x, y) {
  if (!x) {
    x = 10;
  }
  if (!y) {
    y = 20;
  }
  return {
    x,
    y
  };
}

or:

function point(x, y) {
  if (typeof x === "undefined") {
    x = 10;
  }
  if (typeof y === "undefined") {
    y = 20;
  }

  return {
    x,
    y
  };
}

We can write:

function point(x, y) {
  x = x || 10;
  y = y || 20;
  return {
    x,
    y
  };
}

The || operator takes all falsy values and returns the second operand if the first one is falsy.

Primitives are Better than Object Wrappers

Primitives are better than object wrappers.

JavaScript has various kinds of primitive values.

They include boolean, numbers, strings, null and undefined .

null is reported as 'object' with the typeof operator but its defined as a separate type in the ES standard.

The JavaScript standard library also provides constructors for these objects.

So, we can write:

const s = new String("hello");

or:

const str = "hello";

We can extract a character with the square brackets.

For instance, we can write:

s[4]

and we get 'o' .

But if have:

typeof s

it returns 'object' .

But if we have:

typeof str

then we get 'string' .

And if we have:

const s1 = new String("foo");
const s2 = new String("foo");

and if we write:

s1 === s2

it returns false since the references are different.

It can only be equal to itself.

As we can see, this is a problem that we have with string wrapper objects and other primitive wrappers.

Conclusion

We shouldn’t use primitive wrapper objects.

They aren’t useful in our code.

Categories
JavaScript Best Practices

Better JavaScript — No Wrappers and ==

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.

Avoid Primitive Wrappers

We should avoid primitive wrappers so that we don’t have to deal with issues with them.

So we should never have code like:

const s1 = new String("foo");  
const s2 = new String("foo");

Then if we have:

s1 === s2;

then it returns false since they’re different objects.

They are only good for call methods implicitly.

So if we have:

"foo".toUpperCase();

then the string is converted to a string wrapper object and then the toUpperCase method is called on it.

Because primitives can be wrapped with wrappers automatically, we can add properties to them.

For instance, we can write:

"foo".bar = 200;

But if we write:

"foo".bar

we get undefined .

The wrapping is done each time a property is added.

Once that’s done, then wrapped object is discarded immediately.

So we’ll never see the value that we set.

This is another place where JavaScript hide type errors.

Using == or != with Mixed Types

If we want to compare operands with different types, then we’ve to beware that the operands will be converted.

For instance, if w have:

const today = new Date();  
if (form.month.value == (today.getMonth() + 1) &&  
  form.day.value == today.getDate()) {  
  // ...  
}

then the types of the operands will be converted to numbers and compared.

It’s easy to miss the data coercion.

So it’s better to do the conversion explicitly by writing:

const today = new Date();  
if (+form.month.value == (today.getMonth() + 1) &&  
  +form.day.value == today.getDate()) {  
  // ...  
}

Now we know both sides have numbers.

Even better, we should use === instead of == for equality comparison so 2 things with different types will all be false .

So we write:

const today = new Date();  
if (+form.month.value === (today.getMonth() + 1) &&  
  +form.day.value === today.getDate()) {  
  // ...  
}

then we know the comparison will only be done with 2 numbers.

When 2 arguments have the same type, then there’s no difference between == and === .

But if the operands for comparisons are different types, we’ll run into problems.

The type coercion rules aren’t obvious.

They’re also complex.

Computers can’t read our minds, so == will probably use the wrong assumptions.

There are several rules for the == operator.

If the first operand is null and the 2nd is undefined then there’s no coercion.

If the first operand is null or undefined and the 2nd operand is anything other than null or undefined , then there’s no coercion.

If the first operand is a primitive string, number or boolean and the 2nd operand is the Date object, then the primitive is converted to a number.

The Date object is converted to a primitive by trying toString first, then valueOf .

If the first operand is a string, number, or boolean and the 2nd operand is a non-Date objecrt, then the primitive is converted to a number.

And the non-Date object is covert to a primitive by trying toString first, then valueOf .

If both operands are primitive string, number, or boolean, then they’re both converted to numbers.

We just use the === and forget about all these rules.

Conclusion

== or != doesn’t have much benefits and has lots of problems.

=== or !== is better.

Primitive wrappers should also be avoided.

Categories
JavaScript Best Practices

Better JavaScript — No Globals or with

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.

Minimize the User of the Global Object

Global objects go against the principle of modularity.

All the variables are available anywhere.

And anything can modify them.

This is a problem that we have when we work with them.

It’s very hard to trace the values of them since they can be changed by anything.

All the components are coupled together when we use global variables.

This is definitely unacceptable when we have big apps.

The global namespace was the only way for separate parts of a JavaScript app to interact until we have modules.

Therefore, we should use modules to separate the parts and lets us use the part we want.

Also, we should be careful in not to create global variables accidentally.

We can do this if we create a variable with var at the top level.

For instance, we can write:

var x = 1;

And we shouldn’t create function declarations at the top level.

So we shouldn’t have:

function foo() {}

Also, we shoildn’tr accidentally create global variables by assigning something that’s not defined.

So don’t write something like:

foo = 1;

or:

this.foo = 1;

at the top level.

These statements all update the global object.

But we can prevent the last 2 items from creating global variables with struct mode.

We can still use global objects from the JavaScript standard library.

For instance, we can use objects like Array , Number , JSON , etc.

But we definitely shouldn’t create new ones.

And if we want to make code accessible to other parts of an app, we should use modules.

Browsers support modules out of the box and we can use module bundlers to build them into one app.

Always Declare Local Variables

We should always declare local variables.

To do this, we can use the let and const keywords.

They’re all block-scoped so the variable stays within the block.

For instance, don’t write:

function swap(arr, i, j) {
  temp = arr[i];
  arr[i] = arr[j];
  arr[j] = temp;
}

Instead, we write:

function swap(arr, i, j) {
  const temp = arr[i];
  arr[i] = arr[j];
  arr[j] = temp;
}

We add the const before the variable name so that we won’t declare a global variable.

Creating global variables is definitely a bad style.

And creating them accidentally is worse.

We can use a linter to check for these kinds of mistakes automatically.

This is one of the things that linters like ESLint would check for.

Avoid the with Statement

We should never use the with statement in our code.

It doesn’t offer many conveniences, but it does offer lots of unreliability and inefficiency.

The inefficiency comes from the fact that the scope can’t be determined by checking the code.

Therefore, optimization is impossible.

The uncertainty of the scope also means working with what’s inside is confusing.

If we have something like:

function foo(x, y) {
  with(Math) {
    return min(round(x), sqrt(y));
  }
}

then we don’t know if x and y are from the parameters or the Math object.

Therefore, we should never use it.

Conclusion

We should always avoid creating global variables and avoid using the with statement.

Categories
JavaScript Best Practices

Better JavaScript — Methods and this

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.

Store Methods on Prototypes

We should store methods in the prototype of a constructor.

This way, we only store one copy of a method and each instance inherit from the prototype.

For instance, instead of writing:

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

  this.toString = function() {
    return `Person: ${this.name}`;
  };
}

We write:

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

Person.prototype.toString = function() {
  return `Person: ${this.name}`;
};

On the surface, they’re the same.

We get the toString method either way if we create a new Person instance with the new operator.

But under the surface, the second way is more efficient.

Use Closures to Store Private Data

JavaScript can have functions inside functions.

And data inside functions aren’t accessible to the outside.

So we can put private data inside functions.

For instance, if we have:

(() => {
  let x = 1;

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

We have the add function inside the arrow function.

And we get the x value inside the add function.

This way, x won’t be available to the outside.

Store Instance State on Instance Objects

We store instance state on instance objects only.

For instance, we shouldn’t have code like:

function Tree() {}

Tree.prototype = {
  children: [],
  addChild(x) {
    this.children.push(x);
  }
};

Instead, we should write:

function Tree() {
  this.children = [];
}

Tree.prototype = {
  addChild(x) {
    this.children.push(x);
  }
};

The prototype is what Tree inherits from, so we shouldn’t put our state there since it’s shared by all instances of Tree .

Instead, we should add states to the constructor so that they won’t be shared in the instance.

Mutable data are always problematic when they’re shared.

This mistake can be avoided with the class syntax.

So we can write:

class Tree {
  constructor() {
    this.children = [];
  }

  addChild(x) {
    this.children.push(x);
  }
}

This way, there’s no way to create states that are shared by multiple instances.

Implicit Binding of this

this is something that can have different values at different locations.

If it’s at the top level, then this is the global object if strict mode is off and undefined if it’s on.

this is the constructor if it’s in a prototype’s method.

And in a class, then this is the class itself.

So if we have:

function Tree() {
  this.children = [];
}

Tree.prototype = {
  addChild(x) {
    this.children.push(x);
  }
};

then this is the Tree constructor.

If we have callbacks that are traditional functions, then this would be the callback itself.

Therefore, we should be aware of their values so that we won’t refer to the wrong value of this .

Arrow functions don’t bind to their own value of this so they’re great for callbacks.

Conclusion

We should be careful of where to place our constructor states.

Also, the value of this may change depending on context.

Categories
JavaScript Best Practices

Better JavaScript — JavaScript Versions and Numbers

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 Which JavaScript Version We Use

Knowing which JavaScript version we’re using is important.

This is because there’re always improvements with each version.

ES6 is the one version with the most changes.

It makes the code very different from older JavaScript if we use its features.

Also, we got to watch out for non-standard features which may crop up occasionally.

Browser support may be different for new features.

So we should watch out for the feature changes.

Also, API features may also change with different JavaScript engines.

So we want to make sure that the browsers we support have the support for the feature we want.

Otherwise, we need programs like Babel and polyfill libraries to make our app run.

Strict mode is another aspect of JavaScript we have to consider.

It forbids us from making mistakes like assigning to global values like undefined or create global variables accidentally.

It’s supposed to be backward compatible with older code, so we can apply strict mode in parts of the code with the 'use strict' directive.

So we can add:

"use strict";

to the top or we can write:

function foo(x) {
  "use strict";
  // ...
}

With strict mode on, we can write code like:

function foo(x) {
  "use strict";
  var arguments = [];
  // ...
}

Then we get the error ‘Uncaught SyntaxError: Unexpected eval or arguments in strict mode’ .

But if we need to concatenate a file with strict mode with ones that don’t, then we may have problems.

If we have strict mode enabled at the top on one file and another with strict mode enabled to a function, then strict mode is going to be applied everywhere.

To reduce the chance of problems, we should make our code out of the global scope.

So we can write:

file1.js

(function() {
  "use strict";

  function f() {
    // ...
  }
  //...

})()

and:

file2.js

(function() {
  function f() {
    // ...
  }
  //...

})()

file1.js has strict mode on and file2.js doesn’t.

So this is much better.

The functions and variables are also private which is another benefit.

JavaScript’s Floating-Point Numbers

JavaScript only has one kind of number.

They’re all floating-point.

So:

typeof 27;
typeof 9.6;
typeof -2.1;

all return 'number' .

JavaScript numbers are double-precision floating-point numbers.

They’re 64-bit encoding of numbers specified by the IEEE 754 standard.

Most arithmetic operators work with any kind of numbers.

So we can write:

0.1 * 1.9
-99 + 100;
21 - 12.3;

The bitwise operators only work with integers.

For instance, we write:

5 | 1;

and we get 5.

A simple expression can take multiple steps to evaluate.

If we convert a number to a binary string:

(5).toString(2);

toString has the radix 2 which means we convert to binary.

So decimal 5 is converted to binary digits.

it drops the extra 0 bits on the left since they don’t affect the value.

Bitwise operators work the same way.

Integers are converted to bits so they can be operated on.

Conclusion

We should check the JavaScript version we’re supporting so that our app can run.

Also, we should be aware that JavaScript only has one kind of number.