JavaScript Fundamentals: Variables, Types, and Functions
JavaScript Fundamentals: Variables, Types, and Functions
JavaScript has evolved significantly. Modern JavaScript (ES6+) provides better patterns for writing clear, maintainable code.
Variable Declarations
const vs let vs var
// var (avoid - function scoped, hoisted)
var name = 'John';
console.log(typeof name); // 'string'
// let (block scoped, temporal dead zone)
let age = 30;
if (true) {
let age = 25; // Different variable
console.log(age); // 25
}
console.log(age); // 30
// const (block scoped, cannot reassign)
const PI = 3.14159;
// PI = 3; // Error: Assignment to constant variable
// const prevents reassignment, not mutation
const obj = { name: 'John' };
obj.name = 'Jane'; // OK - mutating
// obj = {}; // Error - cannot reassign
Best Practice: Use const by default, let if reassignment needed, never use var.
Data Types
Primitives
// String
const greeting = 'Hello, World!';
const template = `Hello, ${name}!`; // Template literal
// Number (no separate int/float)
const int = 42;
const float = 3.14;
const big = 1e10;
const hex = 0xFF;
// Boolean
const active = true;
const inactive = false;
// null and undefined
const nothing = null; // Intentional absence
let uninitialized; // undefined - not set
Type Checking
// Use typeof carefully
typeof 42 // 'number'
typeof 'hello' // 'string'
typeof true // 'boolean'
typeof undefined // 'undefined'
typeof Symbol('id') // 'symbol'
typeof {} // 'object' (even for null!)
typeof [] // 'object' (arrays are objects)
// Better null check
value === null // Explicit null check
value == null // Checks null or undefined
value === undefined // Explicit undefined check
// Type coercion (dangerous!)
'5' + 3 // '53' (string concatenation)
'5' - 3 // 2 (numeric coercion)
'5' == 5 // true (loose equality)
'5' === 5 // false (strict equality - use this)
Objects and Arrays
Object Literals
// Basic object
const user = {
name: 'John',
age: 30,
email: 'john@example.com'
};
// Accessing properties
console.log(user.name); // 'John'
console.log(user['email']); // 'john@example.com'
// Dynamic properties
const key = 'age';
console.log(user[key]); // 30
// Method shorthand
const person = {
name: 'John',
greet() {
return `Hello, ${this.name}`;
}
};
// Destructuring
const { name, age } = user;
const { name: userName, age: userAge } = user;
// Spread operator
const newUser = { ...user, email: 'newemail@example.com' };
Arrays
// Array creation
const arr = [1, 2, 3, 4, 5];
const mixed = [1, 'hello', true, null];
// Methods
arr.push(6); // Add to end
arr.pop(); // Remove from end
arr.shift(); // Remove from start
arr.unshift(0); // Add to start
// Iteration
arr.forEach(item => console.log(item));
arr.map(x => x * 2); // [2, 4, 6, 8, 10]
arr.filter(x => x > 2); // [3, 4, 5]
arr.find(x => x > 3); // 4
arr.some(x => x > 5); // false
arr.every(x => x > 0); // true
// Destructuring
const [first, second, ...rest] = arr;
Functions
Function Declarations
// Function declaration (hoisted)
function add(a, b) {
return a + b;
}
// Function expression (not hoisted)
const multiply = function(a, b) {
return a * b;
};
// Arrow function (concise syntax)
const divide = (a, b) => a / b;
// Arrow with block body
const calculate = (a, b) => {
const result = a + b;
return result * 2;
};
// Default parameters
const greet = (name = 'Guest') => `Hello, ${name}!`;
// Rest parameters
const sum = (...numbers) => numbers.reduce((a, b) => a + b, 0);
sum(1, 2, 3, 4, 5); // 15
Function Scope
let global = 'global';
function outer() {
let outerVar = 'outer';
function inner() {
let innerVar = 'inner';
console.log(innerVar); // 'inner' - own scope
console.log(outerVar); // 'outer' - parent scope
console.log(global); // 'global' - global scope
}
// console.log(innerVar); // Error - not in scope
inner();
}
outer();
// console.log(outerVar); // Error - not in scope
Closures
// Closure: inner function remembers outer scope
function createCounter() {
let count = 0;
return {
increment() { return ++count; },
decrement() { return --count; },
get() { return count; }
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.get()); // 2
// Practical: Data privacy
function createUser(initialBalance) {
let balance = initialBalance;
return {
deposit(amount) {
balance += amount;
return balance;
},
withdraw(amount) {
if (amount <= balance) {
balance -= amount;
return balance;
}
return 'Insufficient funds';
},
getBalance() {
return balance;
}
};
}
const account = createUser(1000);
console.log(account.deposit(500)); // 1500
console.log(account.withdraw(200)); // 1300
// No way to access balance directly - it's private!
this Binding
// this depends on how function is called
const obj = {
name: 'John',
greet: function() {
console.log(this.name);
},
greetArrow: () => {
console.log(this.name); // undefined - arrow doesn't bind this
}
};
obj.greet(); // 'John' - called on object
const greet = obj.greet;
greet(); // undefined - called without object
// Explicit binding
function introduce() {
return `Hi, I'm ${this.name}`;
}
const person = { name: 'Alice' };
introduce.call(person); // 'Hi, I'm Alice'
introduce.apply(person); // 'Hi, I'm Alice'
const boundIntroduce = introduce.bind(person);
boundIntroduce(); // 'Hi, I'm Alice'
Common Mistakes
// BAD - Using var
var x = 1;
if (true) {
var x = 2;
}
console.log(x); // 2 (unexpected!)
// GOOD - Using const/let
const y = 1;
if (true) {
const y = 2;
}
console.log(y); // 1 (expected)
// BAD - Loose equality
if (value == null) {} // True for both null and undefined
// GOOD - Strict equality
if (value === null || value === undefined) {}
// BAD - Lost this context
const user = {
name: 'John',
printName: function() {
setTimeout(function() {
console.log(this.name); // undefined - wrong this
}, 1000);
}
};
// GOOD - Arrow function preserves this
const user = {
name: 'John',
printName: function() {
setTimeout(() => {
console.log(this.name); // 'John' - correct
}, 1000);
}
};
// BAD - Mutating arguments
function addItem(arr) {
arr.push('item'); // Modifies original
}
// GOOD - Immutable approach
function addItem(arr) {
return [...arr, 'item']; // Returns new array
}
Conclusion
JavaScript fundamentals:
- Use
constby default for immutability - Understand scope and closures for proper encapsulation
- Use arrow functions for cleaner syntax
- Prefer strict equality (
===) - Leverage modern syntax (destructuring, spread operator)