Complex & Weird JavaScript Features Explained

Invalid Date (NaNy ago)


๐Ÿง  Complex & "Weird" JavaScript Features You Should Know

JavaScript is a powerful, dynamic, and highly flexible language โ€” and with that flexibility comes some truly strange behaviors. Here, we dive into the most advanced and weird features of JavaScript that often trip up even seasoned developers.


๐Ÿ”„ 1. Coercion Madness (Type Conversion)

[] + []           // ""
[] + {}           // "[object Object]"
{} + []           // 0 (or "[object Object]" depending on context)
true + false      // 1
null + 1          // 1
undefined + 1     // NaN

โš ๏ธ + is both addition and string concatenation operator depending on operand types. Empty arrays coerce to empty strings. Objects to [object Object].


๐Ÿ” 2. Function Hoisting Oddities

hoisted(); // "Hoisted!"
function hoisted() {
  console.log("Hoisted!");
}
 
notHoisted(); // โŒ TypeError
var notHoisted = function () {
  console.log("Not hoisted");
};

Functions declared with function are fully hoisted. var declarations are hoisted but not their assignments.


๐Ÿ“ฆ 3. Closures Inside Loops

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
} // Logs: 3, 3, 3
 
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
} // Logs: 0, 1, 2

let creates block-scoped variables, preserving loop state. var is function-scoped.


๐Ÿ”„ 4. Object Keys Are Always Strings (Or Symbols)

const a = {};
a[1] = "one";
a[true] = "bool";
a[{}] = "object";
console.log(a); // All keys coerced to strings!

Objects coerce keys to strings unless you use Map which preserves key types.


๐Ÿง  5. this Keyword Quirks

const obj = {
  name: "JS",
  say: function () {
    console.log(this.name);
  },
};
 
const say = obj.say;
say(); // undefined (in strict mode), or global object in non-strict
 
obj.say(); // "JS"

this is dynamic. Arrow functions lexically bind this, while regular functions do not.


๐Ÿ” 6. Double Equals (==) Weirdness

0 == '0'        // true
false == 'false' // false
false == '0'     // true
null == undefined // true
[] == false      // true
[] == ![]        // true

Use === for safety unless you really know what you're doing.


๐Ÿ”ฅ 7. The Temporal Dead Zone (TDZ)

console.log(a); // โŒ ReferenceError
let a = 10;

Variables declared with let and const are hoisted, but not initialized โ€” they live in the TDZ until the line of declaration.


๐Ÿงช 8. typeof null

typeof null; // "object" (Legacy bug)

A long-standing bug that remains for backward compatibility.


๐Ÿงฌ 9. NaN Is Not Equal to Itself

NaN === NaN; // false
Number.isNaN(NaN); // true

Use Number.isNaN instead of ===.


๐ŸŽญ 10. with Statement & Strict Mode

with (Math) {
  console.log(PI);
} // Bad practice

with is disallowed in strict mode and considered bad practice. It makes scope resolution unpredictable.


๐Ÿงฉ 11. Symbol and Hidden Properties

const obj = {};
const key = Symbol("hidden");
obj[key] = 123;
 
console.log(Object.keys(obj)); // []

Symbols allow for non-enumerable, "hidden" properties.


๐Ÿงต 12. Generators and Async Iteration

function* gen() {
  yield 1;
  yield 2;
}
 
for (let val of gen()) {
  console.log(val);
}

Generators pause and resume execution โ€” useful for stateful logic or lazy evaluation.


๐Ÿงจ 13. Proxies

const handler = {
  get: (target, prop) => (prop in target ? target[prop] : "๐Ÿคทโ€โ™‚๏ธ"),
};
 
const obj = new Proxy({}, handler);
console.log(obj.name); // "๐Ÿคทโ€โ™‚๏ธ"

Proxies allow for meta-programming. Can intercept and customize behavior for objects.


๐Ÿง  14. Tagged Template Literals

function tag(strings, ...values) {
  return strings.reduce((acc, s, i) => acc + s + (values[i] || ""), "");
}
 
const result = tag`Hello, ${"JS"}!`;
console.log(result); // "Hello, JS!"

Tags let you modify how template strings are parsed.


๐Ÿงช 15. Object Destructuring with Renaming & Defaults

const { a: alpha = 10, b: beta = 20 } = { a: 5 };
console.log(alpha, beta); // 5, 20