Developer's blog

Go to Notes

The Most Weird Parts of JavaScript

JavaScript is one of the most popular programming languages nowadays. Originally it was created in 10 days by Brendan Eich in 1995 for building simple scripts on the web pages but now it’s used for the development of different types of applications like sophisticated frontend or highload backend or even terminal tools - the one language to rule them all.

JavaScript evolves dramatically during the last 25 years. In 2020 it has a bunch of features that allow developers to write elegant code for solving everyday problems. JavaScript object model differs a lot from other languages like Java or Python. Some people in the community love JavaScript, but it would be futile to deny that the language has some weird moments. In this article, I’ll try to describe the top 3 things in JavaScript that are most weird for me.

3. Inheritance

JavaScript introduces the concept of objects as a collection of related data and functionality. Programs create and manipulate objects to perform different actions and solve software problems. One of the core concepts connected with objects is a prototypal inheritance.

The idea of prototypal inheritance is simple: every object has a prototype and when the program tries to receive one of the object’s properties and doesn’t find any value it tries to find this property in the object’s prototype.

const langs = ['JavaScript', 'Python', 'Java'];
console.log(langs.indexOf('Python')); // 1

In the example above object langs has no indexOf property but it has a prototype equals to Array.prototype which has such property. That is why this code works.

It’s possible to implement some functionality in the prototype and use it in all objects inherited from this prototype:

function Country(name, capital) {
  this.name = name;
  this.capital = capital;
}

Country.prototype.print = function() {
  console.log(`Country ${this.name} with capital ${this.capital}`);
}

const Italy = new Country('Italy', 'Rome');
const Vietnam = new Country('Vietnam', 'Hanoi');
const Dominicana = new Country('Dominican Republic', 'Santo Domingo');

Italy.print();
Vietnam.print();
Dominicana.print();

All objects can use print method because it’s defined in the prototype Country.prototype.

The idea of prototypal inheritance is simple but the devil is in the detail.

Almost every object has Object.prototype in its prototype chain and it can be mutated. This means it’s possible to add or overwrite some behavior for almost all objects:

Object.prototype.sayHello = () => { console.log('Hello!') };
"test string".sayHello(); // Hello!

From the one side, such a feature allows you to implement polyfills (like core-js does) but from the other side, it can affect performance if you accidentally replace native code by the polyfill or even break down some code because you can’t predict who and when will change core objects behavior.

Another operator related to inheritance is instanceof. It allows to check whether an object has some other object in its prototype chain:

function Country(name, capital) {
  this.name = name;
  this.capital = capital;
}

const Italy = new Country('Italy', 'Rome');

console.log(Italy instanceof Country); // true
console.log(Italy instanceof Object); // true

In the code above instanceof checks if Italy object has Country.prototype and Object.prototype in its prototype chain:

Italy.__proto__ === Country.prototype
Italy.__proto__ === Object.prototype
Italy.__proto__.__proto__ === Object.prototype

The tricky question, is it possible to evaluate such code as true?

(a instanceof b) && (b instanceof a);

At first sight, it seems impossible but instanceof checks not objects a and b but a.__proto__ and b.prototype. After such an observation it’s possible to write such code:

const a = function() {};
const b = function() {};

a.__proto__ = b.prototype;
b.__proto__ = a.prototype;

console.log((a instanceof b) && (b instanceof a)); // true

The example above looks like a hack but it’s easy to meet such case in real life:

const a = Object;
const b = Function;

console.log((a instanceof b) && (b instanceof a)); // true

Almost every object in JavaScript has Object.prototype in it’s prototype chain, but at the same time Object is a constructor that is why it has Function.prototype in it’s prototype chain.

Moreover for some objects even such case is possible:

console.log(Object instanceof Object); // true

2. Equality operator (==)

As JavaScript is a dynamically typed language there is a problem of comparing values of different types. E.g. there is a text field and a script expects user to input number but the value of the text field is always a string. Developers can cast a value to a number manually or use an equality operator. Sometimes it works as expected but in some cases, the behavior can be counterintuitive:

'' == []; // true
[] == []; // false
[] == ![]; // true

This example looks strange when array equals to an empty string but not equals to another empty array and even more strange when [] equals ![]. Fortunately, this behavior is described in detail in the language spec or MDN docs:

Equality operator can be tricky that is why I prefer to perform necessary type conversions myself and use strict equality which has a simpler algorithm.

1. document.all

The most insane thing in JavaScript that blows my mind completely is document.all. This property is an HTMLAllCollection rooted at the document node. It returns all of the document’s elements accessible by order as in an array:

document.all[0]; // html node
document.all[1]; // head node

At the same time, it’s possible to use document.all as a function to access elements by identifier like document.getElementById:

document.all('btn'); // equivalent to document.getElementById('btn')

Moreover document.all is the only falsy object accessible to JavaScript:

console.log(typeof document.all); // undefined
console.log(document.all ? 1 : 0); // 0

This was done using [[IsHTMLDDA]] internal slot because of compatibility with older versions of Internet Explorer.

document.all is a proprietary Microsoft extension to the W3C standard. It’s not recommended to use but it still works in modern browsers like Firefox 78 or Google Chrome 83.

– Proprietary extension in a Microsoft’s browser. Take that off, what are you?

– Function, array, object, undefined.

Conclusion

When you start learning a new language there are a lot of weird things especially if the language evolved a lot during the time. If you can’t understand some behavior I suggest reading ECMAScript specification or at least developer.mozilla.org. Also, there is an interesting GitHub repo wtfjs which describes and explains a lot of tricky JavaScript examples.

There is a game called ReturnTrue. I don’t think everyone would like it but if you like competitive programming or want to understand JavaScript better it worth playing.