JavaScript Syntax and Variables
let/const vs var, primitive types, template literals, arrays, objects, destructuring and spread/rest.
let, const, and why not var
Modern JavaScript declares variables with let (reassignable) or const (cannot be reassigned) — both are block-scoped, meaning they only exist inside the nearest enclosing { }:
const name = "Ada"; // cannot be reassigned
let age = 30; // can be reassigned
age = 31; // fine
if (true) {
let scoped = "inside";
console.log(scoped); // "inside"
}
// console.log(scoped); // ReferenceError: scoped is not defined
var, the original way to declare variables, is function-scoped, not block-scoped — it leaks out of if/for blocks, and it's hoisted with a confusing "declared but undefined" state, rather than raising a clear error like let/const do when accessed before declaration. There's essentially no reason to use var in modern code:
if (true) {
var leaked = "I escape the block";
}
console.log(leaked); // "I escape the block" — surprising and error-prone
// let/const throw a clear error instead of silently giving `undefined`:
console.log(notYetDeclared); // ReferenceError (the "temporal dead zone")
let notYetDeclared = 5;
Default to const. Only use let when you know the variable needs to be reassigned (a loop counter, an accumulator). This makes code easier to reason about — seeing const tells you immediately that a value never changes.
Primitive types
JavaScript has seven primitive types:
const str = "hello"; // string
const num = 42; // number (both integers and floats)
const big = 123456789012345678901234567890n; // bigint (arbitrary precision integers)
const flag = true; // boolean
const nothing = null; // null — intentional "no value"
let notSet; // undefined — a declared variable with no assigned value
const id = Symbol("id"); // symbol — a guaranteed-unique value
console.log(typeof num); // "number"
console.log(typeof notSet); // "undefined"
null and undefined are both "empty," but mean different things: undefined is what JavaScript gives you automatically (an unset variable, a missing function argument, a nonexistent object property); null is a value you explicitly assign to say "this is intentionally empty."
Template literals
Backtick-delimited strings support embedded expressions and multi-line text — no more clunky + concatenation:
const name = "Ada";
const age = 30;
const oldWay = "Hello, " + name + "! You are " + age + ".";
const modern = `Hello, ${name}! You are ${age}.`;
const multiLine = `Line one
Line two`;
Arrays
const fruits = ["apple", "banana", "cherry"];
fruits.push("date"); // add to the end
console.log(fruits.length); // 4
console.log(fruits[0]); // "apple"
console.log(fruits.includes("banana")); // true
const doubled = [1, 2, 3].map(n => n * 2); // [2, 4, 6]
const evens = [1, 2, 3, 4].filter(n => n % 2 === 0); // [2, 4]
Objects
const user = {
name: "Ada",
age: 30,
greet() {
return `Hi, I'm ${this.name}`;
},
};
console.log(user.name); // "Ada" — dot access
console.log(user["age"]); // 30 — bracket access (needed for dynamic keys)
user.email = "ada@example.com"; // adding a new property is just an assignment
Destructuring
Destructuring pulls values out of arrays or objects into individual variables in one expression:
const point = [10, 20];
const [x, y] = point;
console.log(x, y); // 10 20
const person = { name: "Ada", age: 30, city: "London" };
const { name, city } = person; // pulls out matching keys by name
console.log(name, city); // Ada London
const { age: userAge = 18 } = person; // rename + default value
Spread and rest
The ... syntax means two different things depending on context: spreading an iterable out, or collecting multiple values in.
// Spread — expand an array/object into individual elements
const nums = [1, 2, 3];
const combined = [...nums, 4, 5]; // [1, 2, 3, 4, 5]
const base = { a: 1, b: 2 };
const extended = { ...base, c: 3 }; // { a: 1, b: 2, c: 3 }
// Rest — collect remaining arguments/properties together
function sum(...numbers) {
return numbers.reduce((total, n) => total + n, 0);
}
console.log(sum(1, 2, 3, 4)); // 10
const { a, ...rest } = extended;
console.log(rest); // { b: 2, c: 3 }
Common mistakes
- Reaching for
varout of habit — its function-scoping and hoisting behavior cause bugs that simply don't exist withlet/const. - Using
==instead of===—==performs type coercion before comparing ("5" == 5istrue), which hides bugs. Always use===/!==unless you have a specific, deliberate reason not to. - Forgetting that spreading an object (
{ ...obj }) is a shallow copy — nested objects/arrays inside it are still shared references, not deep copies.
Interview questions
Q: Why is let/const preferred over var?
let/const are block-scoped (matching how every other C-family language scopes variables), and accessing them before their declaration throws a clear error instead of silently returning undefined. var is function-scoped, leaks out of blocks like if and for, and its hoisting behavior is a frequent source of subtle bugs.
Q: What's the difference between null and undefined?
undefined means a variable has been declared but never assigned a value, or a property/argument simply doesn't exist — it's JavaScript's own default "empty" marker. null is a value a developer explicitly assigns to represent "intentionally no value." null == undefined is true (loose equality treats them as equivalent), but null === undefined is false (they're different types).