14 mysterious JavaScript features worth knowing
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.
Member discussion