Categories
JavaScript Best Practices

Better JavaScript — Prototypes and 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.

Closures are Better than Strings for Encapsulating Code

We should never have code in strings.

If we need to run code, we should put them in a function.

For instance, if we have the repeat function, we should never write something like:

function repeat(n, action) {
  for (let i = 0; i < n; i++) {
    eval(action);
  }
}

eval is always bad because of security, performance, and scoping issues.

Instead, we should write:

function repeat(n, action) {
  for (let i = 0; i < n; i++) {
    action();
  }
}

Then we can use it by writing:

const start = [];
repeat(1000, () => {
  start.push(Date.now());
});

We passed in a callback so that we can run action in our function.

The code should never be in a string.

Instead, it should take a callback function and run it.

Don’t Rely on toString Method of Functions

The toString method doesn’t always return the source code of a function.

It does return the source code most of the time for functions we create.

So if we have:

(function(x) {
  return x + 2;
}).toString();

Then we get:

"function(x) {
  return x + 2;
}"

as a result.

If we use the toString method on built-in native objects, then we won’t see the function’s source code.

For instance, if we have:

Math.max.toString()

Then we get:

"function max() { [native code] }"

If we do something like calling bind to a function:

(function(x) {
  return x + 2;
}).bind(16).toString();

Then we get:

"function(){return n.apply(e,t||arguments)}"

We don’t get back anything meaningful in the string.

Therefore, we can’t rely on toString to always return the source code of a function.

Avoid Nonstandard Stack Inspection Properties

We should never use properties like arguments.callee to get the function that calls another function.

And we shouldn’t use caller property on the property to get the function that called the function even if the property exists.

These aren’t standard and they should be avoided.

So we should write code like:

function revealCaller() {
  return revealCaller.caller;
}

These are all disallowed in ES5 strict mode, so this is a benefit of enabling it.

If we have:

function foo() {
  "use strict";
  return foo.caller;
}

We get ‘Uncaught TypeError: ‘caller’, ‘callee’, and ‘arguments’ properties may not be accessed on strict mode functions or the arguments objects for calls to them’.

Get the Difference Between prototype, getPrototypeOf, and proto

These objects are separate from each other.

prototype and __proto__ all get the prototype of an object and set it.

And getPrototypeOf is a method to get the prototype of an object.

__proto__ has become a standard so we can use it like prototype .

prototype is usually used for instance methods.

For instance, we can create a constructor by writing:

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

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

toString is an instance method of the constructor.

Then we can write:

const person = new Person("james");

to create a new Person instance.

The __proto__ property can be used like a regular property, so we can write:

person.__proto__

to get the prototype of the object.

We can also assign a value of it and use it as a property of an object.

Object.getPrototypeOf can be used to get the prototype of an object, so we can write:

Object.getPrototypeOf(person)

to get the prototype.

A prototype is a JavaScript object that an object inherits from.

Conclusion

We should never run code in strings in our code.

Also, we shouldn’t use caller and callee in our code.

And we can get and set object prototypes in various ways.

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.