Fundamentals

6 min read
Rapid overview

JavaScript Fundamentals

Core JavaScript concepts essential for frontend development.

Table of Contents

Variables and Scope

Variable Declarations

// var - function-scoped, hoisted
var x = 10;

// let - block-scoped
let y = 20;

// const - block-scoped, cannot be reassigned
const z = 30;

Scope Types

Global Scope:

var globalVar = 'accessible everywhere';

function test() {
  console.log(globalVar); // accessible
}

Function Scope:

function example() {
  var functionScoped = 'only in function';
  console.log(functionScoped); // works
}

console.log(functionScoped); // ReferenceError

Block Scope:

{
  let blockScoped = 'only in block';
  const alsoBlockScoped = 'also only in block';
}

console.log(blockScoped); // ReferenceError

Hoisting

// var hoisting
console.log(x); // undefined (declaration hoisted)
var x = 5;

// let/const hoisting (Temporal Dead Zone)
console.log(y); // ReferenceError
let y = 10;

// function hoisting
greet(); // works!
function greet() {
  console.log('Hello!');
}

Data Types

Primitives

// String
const str = 'Hello';
const template = `World ${str}`;

// Number
const num = 42;
const float = 3.14;
const nan = NaN;
const inf = Infinity;

// Boolean
const bool = true;

// Undefined
let undef;
console.log(undef); // undefined

// Null
const nothing = null;

// Symbol (ES6)
const sym = Symbol('unique');

// BigInt (ES2020)
const bigInt = 9007199254740991n;

Objects

// Object literal
const person = {
  name: 'John',
  age: 30,
  greet() {
    console.log(`Hi, I'm ${this.name}`);
  }
};

// Array
const arr = [1, 2, 3];

// Function
const func = function() {};

// Date
const date = new Date();

// RegExp
const regex = /pattern/;

Type Checking

typeof 'hello'; // 'string'
typeof 42; // 'number'
typeof true; // 'boolean'
typeof undefined; // 'undefined'
typeof {}; // 'object'
typeof []; // 'object' (arrays are objects!)
typeof null; // 'object' (historical bug)
typeof function() {}; // 'function'

// Better array check
Array.isArray([]); // true

// Better null check
value === null;

Functions

Function Declarations

// Function declaration
function add(a, b) {
  return a + b;
}

// Function expression
const subtract = function(a, b) {
  return a - b;
};

// Arrow function
const multiply = (a, b) => a * b;

// Arrow function with block
const divide = (a, b) => {
  if (b === 0) throw new Error('Division by zero');
  return a / b;
};

Arrow Functions vs Regular Functions

// Regular function - has its own 'this'
const obj1 = {
  value: 10,
  getValue: function() {
    return this.value;
  }
};

// Arrow function - inherits 'this' from parent scope
const obj2 = {
  value: 10,
  getValue: () => this.value // 'this' is from parent scope, not obj2
};

// Arrow functions cannot be constructors
const Person = (name) => {
  this.name = name;
};
// new Person('John'); // TypeError

Default Parameters

function greet(name = 'Guest') {
  console.log(`Hello, ${name}!`);
}

greet(); // Hello, Guest!
greet('Alice'); // Hello, Alice!

Rest Parameters

function sum(...numbers) {
  return numbers.reduce((total, num) => total + num, 0);
}

sum(1, 2, 3, 4); // 10

Closures

A closure is a function that remembers variables from its outer scope.

function createCounter() {
  let count = 0; // private variable

  return {
    increment() {
      count++;
      return count;
    },
    decrement() {
      count--;
      return count;
    },
    getCount() {
      return count;
    }
  };
}

const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.getCount(); // 2

Practical Use: Module Pattern

const calculator = (function() {
  // Private variables
  let result = 0;

  // Public API
  return {
    add(x) {
      result += x;
      return this;
    },
    subtract(x) {
      result -= x;
      return this;
    },
    getResult() {
      return result;
    },
    reset() {
      result = 0;
      return this;
    }
  };
})();

calculator.add(10).subtract(3).getResult(); // 7

Prototypes and Inheritance

Prototype Chain

function Person(name) {
  this.name = name;
}

Person.prototype.greet = function() {
  console.log(`Hello, I'm ${this.name}`);
};

const john = new Person('John');
john.greet(); // Hello, I'm John

// Prototype chain
console.log(john.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true

ES6 Classes

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} makes a sound`);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }

  speak() {
    console.log(`${this.name} barks`);
  }

  getBreed() {
    return this.breed;
  }
}

const dog = new Dog('Rex', 'Labrador');
dog.speak(); // Rex barks

Asynchronous JavaScript

Callbacks

function fetchData(callback) {
  setTimeout(() => {
    callback({ data: 'Some data' });
  }, 1000);
}

fetchData((result) => {
  console.log(result.data);
});

Promises

function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const success = true;
      if (success) {
        resolve({ data: 'Some data' });
      } else {
        reject(new Error('Failed to fetch'));
      }
    }, 1000);
  });
}

fetchData()
  .then(result => console.log(result.data))
  .catch(error => console.error(error));

Async/Await

async function getData() {
  try {
    const result = await fetchData();
    console.log(result.data);
    return result;
  } catch (error) {
    console.error('Error:', error);
  }
}

getData();

Event Loop

console.log('1');

setTimeout(() => console.log('2'), 0);

Promise.resolve().then(() => console.log('3'));

console.log('4');

// Output: 1, 4, 3, 2
// Microtasks (Promises) execute before macrotasks (setTimeout)

Modules

ES6 Modules

Exporting:

// math.js
export const PI = 3.14159;

export function add(a, b) {
  return a + b;
}

export default class Calculator {
  multiply(a, b) {
    return a * b;
  }
}

Importing:

// main.js
import Calculator, { PI, add } from './math.js';

console.log(PI); // 3.14159
console.log(add(2, 3)); // 5

const calc = new Calculator();
console.log(calc.multiply(4, 5)); // 20

CommonJS (Node.js)

Exporting:

// math.js
module.exports = {
  PI: 3.14159,
  add(a, b) {
    return a + b;
  }
};

Importing:

// main.js
const math = require('./math');

console.log(math.PI);
console.log(math.add(2, 3));

Best Practices

  1. Use const by default, let when you need to reassign, avoid var
  2. Prefer arrow functions for callbacks and short functions
  3. Use async/await over raw promises when possible
  4. Avoid callback hell - use promises or async/await
  5. Use strict mode ('use strict';)
  6. Check for null/undefined before accessing properties
  7. Use destructuring for cleaner code
  8. Avoid modifying prototypes of built-in objects

Common Patterns

Destructuring

// Array destructuring
const [first, second, ...rest] = [1, 2, 3, 4, 5];

// Object destructuring
const { name, age, city = 'Unknown' } = person;

// Function parameter destructuring
function greet({ name, age }) {
  console.log(`${name} is ${age} years old`);
}

Spread Operator

// Array spread
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]

// Object spread
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 }; // { a: 1, b: 2, c: 3 }

Optional Chaining

const user = {
  profile: {
    name: 'John'
  }
};

console.log(user?.profile?.name); // 'John'
console.log(user?.address?.city); // undefined (no error)

Nullish Coalescing

const value = null ?? 'default'; // 'default'
const num = 0 ?? 'default'; // 0 (only null/undefined trigger default)