4 min read

14 mysterious JavaScript features worth knowing

14 mysterious JavaScript features worth knowing
Photo by Gabriel Heinzer / Unsplash

JavaScript is a powerful and flexible programming language, but it also has some unique features and behavior that can be considered "weird" or "non-intuitive" to some developers. Here are a few examples:

Automatic type coercion:

JavaScript is a loosely typed language, which means that the interpreter will automatically convert values from one type to another when necessary. This can lead to unexpected results, for example:

console.log(1 + "2");  // "12" (string concatenation)
console.log([] + []); // "" (empty string)
console.log([1,2,3] == "1,2,3"); // true (type coercion)

Equality comparison:

JavaScript has two types of equality comparison: "==" and "===". The "==" operator performs type coercion if necessary, while the "===" operator compares values without coercion. This can lead to unexpected results, for example:

console.log(1 == true);  // true (type coercion)
console.log(1 === true); // false (no coercion)
console.log(null == undefined); // true
console.log(null === undefined); // false

Hoisting:

In JavaScript, variable and function declarations are "hoisted" to the top of their scope, which means that they are accessible before they are declared. This can lead to unexpected results, for example:

console.log(x);   // undefined (x is hoisted)
var x = 1;

console.log(y);   // ReferenceError: y is not defined
let y = 1;

Global variables:

In JavaScript, variables that are not declared inside a function are considered global variables and are accessible from anywhere in the code. This can lead to naming conflicts and unexpected results, for example:

var x = 1;
function test() {
  console.log(x);   // 1 (x is global)
}
test();

The 'this' keyword:

In JavaScript, the value of this keyword is determined by how a function is called, not where it is defined. This can lead to unexpected results, for example:

const obj = {
  name: "John",
  printName: function() {
    console.log(this.name);
  }
};

const print = obj.printName;
print();  // undefined (this is not bound to obj)

Function scope:

In JavaScript, function scope is used instead of block scope. This can lead to unexpected results, for example:

for (var i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i);
  }, 100);
}
// 3 3 3 (i is in the same scope)

for (let i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i);
  }, 100);
}
// 0 1 2 (i is in different scope)

Prototype-based inheritance:

JavaScript is a prototype-based language, which means that objects inherit properties and methods from other objects through prototypes. This can be different from traditional class-based inheritance and can lead to unexpected results, for example:

const animal = {
  eats: true,
  walk() {
    console.log("Animal can walk");
  }
};
const rabbit = Object.create(animal);
rabbit.jumps = true;
console.log(rabbit.eats);  // true (inherited from animal)
console.log(rabbit.jumps); // true (defined on rabbit)

Automatic semicolon insertion:

JavaScript's automatic semicolon insertion (ASI) feature can lead to unexpected results if not used correctly. For example:

const x = 5
console.log(x) // 5

In this example, JavaScript inserts a semicolon after the const x = 5 statement, which is treated as a separate statement from the console.log(x). As a result, x is assigned the value 5 instead of the intended console.log(x) statement.

Function expressions vs function declarations:

The difference in how they are handled by JavaScript's hoisting feature can lead to unexpected results if not used correctly. For example:

foo(); // ReferenceError

const foo = function() {
  console.log('bar');
};

In this example, the function is assigned to a variable foo but due to hoisting the variable is hoisted but the assignment is not and thus the reference error.

The NaN value:

JavaScript has a special NaN value, which stands for "not a number." When a mathematical operation cannot produce a valid number, it returns NaN. This can lead to unexpected results, for example:

console.log(0 / 0); // NaN
console.log(isNaN(NaN)); // true
console.log(NaN == NaN); // false

Implicit type conversion:

JavaScript can perform implicit type conversion in certain situations, such as when comparing values or using certain operators. For example:

console.log("5" == 5); // true
console.log("5" === 5); // false
console.log(true + 1); // 2
console.log([] + {}); // "[object Object]"

In these examples, JavaScript performs an implicit type conversion, which can lead to unexpected results if not taken into account.

Property access on null and undefined:

In JavaScript, trying to access a property on a null or undefined value will not throw an error, but instead will return undefined. For example:

console.log(null.x); // undefined
console.log(undefined.x); // undefined
console.log({}.x); // undefined

In these examples, trying to access a property on a null or undefined value will not throw an error, but it can lead to unexpected results if not handled properly.

The == operator:

JavaScript's == operator compares values for equality, but it can perform type coercion if the operands are of different types. This can lead to unexpected results, for example:

console.log('' == 0); // true
console.log(null == undefined); // true
console.log([] == false); // true

In these examples, JavaScript's == operator performs type coercion and returns true even though the values are not equal in the traditional sense.

Function scope:

JavaScript uses function scope, which means that variables declared within a function are only accessible within that function. However, variables declared with the var keyword are also accessible within the entire function scope, which can lead to unexpected results. For example:

function test() {
  var x = 1;
  if (true) {
    var x = 2;
    console.log(x); // 2
  }
  console.log(x); // 2
}
test();

In this example, the variable x is declared twice within the same function scope, and the second declaration overwrites the first. This can lead to unexpected results if not handled properly.

Conclusion:

These are examples of unique features and behavior of JavaScript that can be considered "weird" or "non-intuitive" to some developers. Understanding these features can help you write more efficient and effective code, and avoid potential bugs in your JavaScript programs.