Javascript is weird, or is it?
By: Alexander Joseph
Tags: Software Development, Programming, JavaScript

Photo by Sincerely Media on Unsplash

Javascript, a programming language with humble beginnings, mostly used for making silly interactions on websites has now become ubiquitous with applications ranging from IoT to AI and Machine learning, ranking as the most popular programming language for 8 years in a row according to Stack Overflow’s Developer Survey. If you can think of it, Javascript can probably do it.

Despite its popularity, a lot of people like to hate on Javascript, they seem to think it is weird, super quirky, and even unpredictable. This narrative is so prevalent that a lot of blog posts would even go as far as recommending that Javascript programmers completely avoid entire aspects of the language like Types and Coercion because "It will bite you in the a**". I agreed with some of these sentiments when I was just starting out but the more I learned about the language, the more I grew to understand and take advantage of what a lot of people would term to be its shortcomings.

Javascript isn’t perfect (no programming language is) but it is anything but unpredictable. The Javascript specification clearly defines the behaviour of the language and a deep understanding of the inner workings of Javascript will enable a programmer to take advantage of its features to craft complex and scalable applications.

In the article, we will attempt to explain the reasoning behind certain ‘oddities’ in Javascript and I hope this encourages the reader to dig deeper into Javascript and truly appreciate how powerful the language is.

I found these slides online some time ago and I really enjoyed trying to figure out the solution to some of these questions so we will attempt to do that together here

Coercion

Arithmetic Operations on Strings and Numbers

1 + "1" // results in "11" 9 + '99' + 100 // results in "999100" 'hello' + 'goodbye' // results in "hellogoodbye" 12 / "6" === 2 // true 12 - "6" === 6 // true 12 * "6" === 72 // true 12 + "6" === 18 // false

The results might be surprising to some but it is quite straightforward. When performing the addition operation on primitives in Javascript, if anyone of the values is a string, the compiler calls the native ToString implementation on both values coercing the non-string into a string and performs a string concatenation and in the case of other arithmetic operations like Subtraction, multiplication and division, the ToNumber implementation is invoked.

Arithmetic Operations on Arrays

["chris", "kapehe"] + ["mo", "bella"] // "chris,kapehemo,bella" ["chris", "kapehe"] - ["mo", "bella"] // NaN

This is a more interesting example and can be a major source of confusion but once again, it can be easily understood by applying the algorithm from the Javascript specification.

["chris", "kapehe"] + ["mo", "bella"] /**

* When the Javascript compiler comes across this expression,

* it invokes the native ToPrimitive implementation on both sides

* of the expression and the expression becomes;

*/ "chris,kapehe" + "mo,bella" // And from what we know about the addition operations on strings, this results in a string concatenation; "chris,kapehemo,bella" // For the subtraction case, we know that the native ToNumber implementation will be invoked resulting in NaN - NaN // NaN

See:

https://www.ecma-international.org/ecma-262/5.1/#sec-11.6

https://www.ecma-international.org/ecma-262/5.1/#sec-11.5

== vs ===

The == operator is another piece of functionality in Javascript that has a bad rap. Countless blog posts and tutorials recommend avoiding the operator at all costs and only use === as that is 'safer' since it does not attempt any form of Coercion.

new Array(3).toString() === ",," // true ',,' === ['', '', ''] // false ',,' == ['', '', ''] // true

The first case here is quite straightforward as we already know what results from calling ToString on an array. Let's take a close look at the second and third cases;

/**

* This behaves as most would expect, a String is not the same as an * Array so this should be false

*/ ',,' === ['', '', ''] // false /**

* This might come as a surprise to most people but it is quite

* straightforward. When the Javascript compiler encounters this,

* it tries to get the Right-hand side and Left-hand side to be of

* the same type so it invokes ToPrimitive on the Array

*/ ',,' == ['', '', ''] // true // this then becomes ',,' == ',,' // And now since both sides of the expression are of the same type and value, we get `true` as the result.

With a solid understanding of coercion, it becomes clear that the == operator can indeed be more powerful than the === and its behaviour is identical to the === operator when the types match.

A classic example of where the == shines is when we need to check for null or undefined

// Instead of doing this

if (someValue === null || someValue == undefined) // We could simply do

if (someValue == null) //or

if (someValue == undefined)

See: https://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3

Scope and Hoisting

Scope

The concept of scope in Javascript is one that trips up even experienced Javascript developers and almost everyone that writes Javascript has at one point or the other experienced a WTF moment regarding Javascript’s scope system. Lexical scoping is probably one of Javascript’s best features and having a good understanding of how it works can supercharge a developer’s problem-solving abilities.

Let’s look through a few examples from the slides;

// Since `someVariable` has not yet been declared within this scope, an error is thrown console.log(someVariable) // Uncaught ReferenceError /**

* The `typeof` operator is special in that it can access an

* undeclared variable without throwing an error. This means that

* the `typeof` operator will always return a string no matter what

*/



console.log(typeof myVariable) // "undefined" /**

* Since we do not get block scoping with the `var` keyword,

* the declaration of the `x` variable in the `if` block propagates

* all through the function scope and the new value of `x` is 10

*/ function doSomethingCool() {

var x = 5; if (true) {

var x = 10;

console.log(x); // 10

} console.log(x); // 10

} doSomethingCool(); /**

* With the `let` keyword, we now have block scoping and so

* within the block scope created by the `if` statement,

* we have a variable `x` whose value is 10

* while the value of `x` remains 5 in the outer function scope

*/ function doSomethingCool() {

let x = 5; if (true) {

let x = 10;

console.log(x); // 10

} console.log(x); // 5

} doSomethingCool();

Hoisting

This example below is a classic example of hoisting in Javascript. It is often described as Javascript moving certain pieces of code to the top of a particular scope but this is not entirely accurate.

A better way to think of this is that Javascript makes two passes over the code, the first to set up the scopes, process formal declarations, etc, and the second to execute the code.

/**

* In the 'processing' phase, we skip the invocation of `sayWhatup`

* and process the formal declaration of the `sayWhatup` function

* then at the execution stage, we find that `sayWhatup` has already * been declared and so we do not get a ReferenceError.

*/ sayWhatup(); // alert function sayWhatup() {

alert('whatup')

}

Here is another example that makes this point clearer

/**

* In the 'processing' phase, we skip the invocation of `log` and

* process the formal declaration of the `message` variable.

* Since variable assignment happens during the execution stage,

* `message` defaults to undefined, and that is what we see

* when the code is executed.

*/ console.log(message); // undefined var message = 'HEY everybody!';

Take some time to work out the other examples here: https://slides.com/chrisoncode/javascript-is-weird and see if you can figure them out.

Javascript is an amazing language, spend some time understanding how it really works, read the specification and it will help you appreciate how powerful the language really is.