JavaScript 6 min read

JavaScript Primer: Essential Concepts

Master core JavaScript concepts including higher-order functions, arrow functions, closures, and the spread operator with practical examples.

MR

Moshiour Rahman

· Updated May 9, 2026

Overview

JavaScript has evolved significantly with ES6 and beyond. This primer covers essential concepts that every JavaScript developer should know: higher-order functions, arrow functions, closures, and modern operators.

Functions as First-Class Citizens

In JavaScript, functions are objects. This means you can:

  • Assign them to variables
  • Pass them as arguments
  • Return them from other functions

Passing Functions as Arguments

function greet(nameFunction) {
  return "Hello " + nameFunction() + "!";
}

const result = greet(function() {
  return "TechyOwls";
});

console.log(result);
// Output: Hello TechyOwls!

Chaining Functions

Functions can call other functions, enabling powerful composition:

function createMessage(nameFunction) {
  return "Hello " + nameFunction() + "!";
}

function displayMessage(nameFunction, printFunction) {
  printFunction(createMessage(nameFunction));
}

displayMessage(
  function() { return "TechyOwls"; },
  console.log
);
// Output: Hello TechyOwls!

Arrow Functions

Arrow functions (also called “fat arrow” or “lambda expressions”) provide a shorter syntax for writing functions.

Basic Syntax

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

// Arrow function
const add = (a, b) => a + b;

// With single parameter (parentheses optional)
const double = x => x * 2;

// With no parameters
const sayHello = () => "Hello!";

Converting Our Example

// Before
function createMessage(nameFunction) {
  return "Hello " + nameFunction() + "!";
}

function displayMessage(nameFunction, printFunction) {
  printFunction(createMessage(nameFunction));
}

// After
const createMessage = nameFunction => "Hello " + nameFunction() + "!";
const displayMessage = (nameFunction, printFunction) =>
  printFunction(createMessage(nameFunction));

displayMessage(() => "TechyOwls", console.log);
// Output: Hello TechyOwls!

When to Use Arrow Functions

Use arrow functions for:

  • Callbacks and inline functions
  • Array methods (map, filter, reduce)
  • Short, simple functions

Don’t use for:

  • Object methods (no this binding)
  • Constructors
  • Functions that need arguments object

Closures

A closure is when an inner function has access to variables from its outer function, even after the outer function has returned.

Basic Closure Example

function createGreeting(name) {
  const weather = "sunny";  // Local variable

  function innerGreeting() {
    return `Hello ${name}! Today is ${weather}.`;
  }

  return innerGreeting();
}

console.log(createGreeting("TechyOwls"));
// Output: Hello TechyOwls! Today is sunny.

Practical Closure: Counter

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

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

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

The count variable is “closed over” - it persists between function calls but isn’t accessible from outside.

Closure Pitfall: Loop Variables

// Problem: All buttons log 5
for (var i = 0; i < 5; i++) {
  setTimeout(() => console.log(i), 100);
}
// Output: 5 5 5 5 5

// Solution 1: Use let
for (let i = 0; i < 5; i++) {
  setTimeout(() => console.log(i), 100);
}
// Output: 0 1 2 3 4

// Solution 2: Create closure
for (var i = 0; i < 5; i++) {
  ((j) => {
    setTimeout(() => console.log(j), 100);
  })(i);
}
// Output: 0 1 2 3 4

Working with Strings

JavaScript provides useful string methods:

Common String Methods

const message = "Hello TechyOwls!";

// charAt - get character at index
message.charAt(0);           // "H"
message.charAt(6);           // "T"

// concat - join strings
"Hello ".concat("World");    // "Hello World"

// indexOf - find position
message.indexOf("T");        // 6
message.indexOf("x");        // -1 (not found)

// includes - check if contains
message.includes("Techy");   // true

// slice - extract portion
message.slice(0, 5);         // "Hello"
message.slice(6);            // "TechyOwls!"

// split - convert to array
message.split(" ");          // ["Hello", "TechyOwls!"]

// toLowerCase/toUpperCase
message.toLowerCase();       // "hello techyowls!"
message.toUpperCase();       // "HELLO TECHYOWLS!"

Template Literals

Use backticks for string interpolation:

const name = "TechyOwls";
const age = 25;

// Old way
const message = "Hello " + name + ", you are " + age + " years old.";

// Template literal (much cleaner!)
const message = `Hello ${name}, you are ${age} years old.`;

// Multiline strings
const html = `
  <div class="card">
    <h2>${name}</h2>
    <p>Age: ${age}</p>
  </div>
`;

The Spread Operator

The spread operator (...) expands arrays or objects.

Spreading Arrays as Arguments

function printItems(num, str, bool) {
  console.log(`Number: ${num}`);
  console.log(`String: ${str}`);
  console.log(`Boolean: ${bool}`);
}

const myArray = [100, "Adam", true];

// Old way
printItems(myArray[0], myArray[1], myArray[2]);

// With spread operator
printItems(...myArray);

Concatenating Arrays

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];

// Combine arrays
const combined = [...arr1, ...arr2];
// Output: [1, 2, 3, 4, 5, 6]

// Insert into array
const inserted = [0, ...arr1, 3.5, ...arr2, 7];
// Output: [0, 1, 2, 3, 3.5, 4, 5, 6, 7]

Copying Arrays

const original = [1, 2, 3];

// Shallow copy
const copy = [...original];

copy.push(4);
console.log(original); // [1, 2, 3] - unchanged
console.log(copy);     // [1, 2, 3, 4]

Spreading Objects

const user = { name: "John", age: 30 };

// Copy and extend
const extendedUser = { ...user, email: "john@example.com" };
// { name: "John", age: 30, email: "john@example.com" }

// Override properties
const updatedUser = { ...user, age: 31 };
// { name: "John", age: 31 }

Rest Parameters

The ... syntax also collects remaining arguments:

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

console.log(sum(1, 2));        // 3
console.log(sum(1, 2, 3, 4));  // 10

Summary

ConceptPurposeExample
Higher-order functionsFunctions as valuesarray.map(fn)
Arrow functionsShorter syntaxx => x * 2
ClosuresPrivate stateCounter pattern
Template literalsString interpolation`Hello ${name}`
Spread operatorExpand arrays/objects[...arr]
Rest parametersCollect arguments(...args)

Conclusion

These JavaScript concepts are fundamental to modern development:

  • Functions are first-class citizens
  • Arrow functions provide cleaner syntax
  • Closures enable private state
  • Spread/rest operators simplify array manipulation

Key Takeaways:

  • Use arrow functions for callbacks
  • Leverage closures for encapsulation
  • Prefer template literals over concatenation
  • Use spread for immutable operations
MR

Moshiour Rahman

Software Architect & AI Engineer

Share:
MR

Moshiour Rahman

Software Architect & AI Engineer

Enterprise software architect with deep expertise in financial systems, distributed architecture, and AI-powered applications. Building large-scale systems at Fortune 500 companies. Specializing in LLM orchestration, multi-agent systems, and cloud-native solutions. I share battle-tested patterns from real enterprise projects.

Related Articles

Comments

Comments are powered by GitHub Discussions.

Configure Giscus at giscus.app to enable comments.