Intermediate JavaScript Guide

1. Functions in JavaScript

1.1 Callbacks

A callback function is a function passed into another function as an argument, to be executed at a later time or in response to an event. This is a fundamental concept in asynchronous JavaScript (e.g., handling responses from fetch calls, setTimeout, event listeners, etc.).

<details> <summary>Example of a callback</summary>

function greet(name) {

  console.log(“Hello, ” + name + “!”);

}

function processUserInput(callback) {

  const userName = “Alice”;

  callback(userName);

}

// Passing greet as a callback

processUserInput(greet);

</details>

1.2 Return Statement

The return keyword exits a function and optionally returns a value. Once the interpreter hits return, no subsequent statements in that function are executed.

<details> <summary>Example of return usage</summary>

function add(a, b) {

  return a + b;

  // Any code here won’t be executed

}

</details>

1.3 Arrow Functions

Arrow functions (introduced in ES6) offer a more concise syntax for writing function expressions. They also have lexical this binding, meaning they capture the this value from their enclosing context rather than having their own.

<details> <summary>Example of an arrow function</summary>

// Traditional function expression

const double = function (x) {

  return x * 2;

};

// Arrow function equivalent

const doubleArrow = x => x * 2;

console.log(double(2));      // 4

console.log(doubleArrow(2)); // 4

</details>

1.4 Console Logging

console.log() is primarily used for debugging and logging information to the browser’s console or Node.js terminal. It does not affect program flow but is invaluable for checking variable values or tracking execution steps.

<details> <summary>Usage example</summary>

function greet(name) {

  console.log(“Hello,”, name);

}

greet(“Bob”); // Logs: “Hello, Bob”

</details>

1.5 Creating Arrays

In JavaScript, arrays are commonly created using bracket notation ([]) or the Array constructor.

<details> <summary>Examples of array creation</summary>

// Bracket notation

let fruits = [“apple”, “banana”, “cherry”];

// Using the Array constructor

let numbers = new Array(1, 2, 3, 4);

</details>


2. Array Operations

2.1 Joining Arrays

You can combine two or more arrays using methods like concat(). This creates a new array without modifying the existing ones.

<details> <summary>Example of array concatenation</summary>

const arr1 = [1, 2];

const arr2 = [3, 4];

const combined = arr1.concat(arr2);

console.log(combined); // [1, 2, 3, 4]

</details>

2.2 Array Methods: map() vs. forEach()

  • Array.map() creates a new array with the results of applying a provided function to every element. It is used for transformation.
  • Array.forEach() does not return a new array. It executes a provided function once for each element, typically used for side effects (e.g., logging, updating something outside the array).

<details> <summary>Comparison example</summary>

const nums = [1, 2, 3];

// Using map()

const doubled = nums.map(num => num * 2); 

console.log(doubled); // [2, 4, 6]

console.log(nums);    // [1, 2, 3] (unchanged)

// Using forEach()

nums.forEach(num => console.log(num * 2));

// logs: 2, 4, 6

// but nums array remains [1, 2, 3], and no new array is returned

</details>


3. Functions and Closures

3.1 Function Declarations vs. Function Expressions

Function Declaration: Defined with the function keyword at the start. They are hoisted, meaning they can be called before they are written in the code.
function greet() {

  console.log(“Hello!”);

}

Function Expression: Assigned to a variable. Not hoisted in the same way, so you cannot call it before you define it.
const greetExpression = function() {

  console.log(“Hello again!”);

};

3.2 Closures

A closure gives you access to an outer function’s scope from an inner function, even after the outer function has finished executing. This allows private state within JavaScript functions.

<details> <summary>Example demonstrating closures</summary>

function createCounter() {

  let count = 0;

  return function() {

    count++;

    console.log(count);

  };

}

const counterA = createCounter();

counterA(); // 1

counterA(); // 2

const counterB = createCounter();

counterB(); // 1

</details>


4. Loops and Iteration

4.1 The for…of Loop

The for…of loop iterates over iterable objects (such as arrays, strings, or anything with a Symbol.iterator). It gives direct access to the items themselves rather than indices.

<details> <summary>Example of for…of</summary>

const colors = [“red”, “green”, “blue”];

for (const color of colors) {

  console.log(color);

}

// Logs: “red”, “green”, “blue”

</details>

4.2 for…in vs. for…of

  • for…in loops over the enumerable property names of an object (or array, though typically used for objects).
  • for…of loops over the values of an iterable object (like arrays, strings, maps, etc.).

<details> <summary>Example of for…in vs. for…of on arrays</summary>

const arr = [“a”, “b”, “c”];

// for…in

for (let index in arr) {

  console.log(index); // 0, 1, 2

}

// for…of

for (let value of arr) {

  console.log(value); // “a”, “b”, “c”

}

</details>


5. Understanding the this Keyword

5.1 this in Regular Functions

In a regular (non-arrow) function, this is determined by how the function is called. For instance:

  • If invoked as a method of an object, this refers to that object.
  • If invoked in the global scope (non-strict mode), this refers to the global object (window in browsers).
  • In strict mode, calling a regular function without an object binding sets this to undefined.

<details> <summary>Example</summary>

const person = {

  name: “Jane”,

  greet: function() {

    console.log(“My name is ” + this.name);

  }

};

person.greet(); // “My name is Jane”

const greetReference = person.greet;

greetReference(); // “My name is undefined” in strict mode

                 // or “My name is [object Window]” in non-strict mode

</details>


6. Object Methods

6.1 Object.entries()

Object.entries(obj) returns an array of key-value pairs from obj. It’s useful when you need to iterate over both keys and values.

<details> <summary>Example</summary>

const user = { name: “Alice”, age: 25 };

const entries = Object.entries(user);

console.log(entries); // [[“name”, “Alice”], [“age”, 25]]

</details>


7. Advanced Scope

In JavaScript, scope primarily falls into:

  • Global scope: Variables accessible everywhere.
  • Function scope: Variables declared within a function using var are scoped to that function.
  • Block scope: Variables declared with let or const within a block {}.

Closures enable variables to persist even after the function in which they were declared has returned. This also intersects with advanced patterns like the module pattern and IIFEs (Immediately Invoked Function Expressions) for data privacy.

8. String Manipulation

8.1 The repeat() Method

string.repeat(count) constructs and returns a new string which contains the specified number of copies of the string on which it was called.

<details> <summary>Example of repeat()</summary>

console.log(“ha”.repeat(3)); // “hahaha”

</details>


Summary of Key Points

  1. Functions:
    • Callbacks allow asynchronous execution.
    • Arrow functions offer concise syntax and lexical this.
    • The return keyword ends function execution and optionally outputs a value.
  2. Closures:
    • Inner functions retain access to the outer function’s scope even after the outer function completes.
  3. Array Methods:
    • concat() merges arrays into a new one.
    • map() creates a new array based on a transformation, while forEach() just iterates without returning a new array.
  4. Loops:
    • for…of iterates over iterable objects, returning each element.
    • for…in iterates over object keys or array indices.
  5. this Keyword:
    • Refers to the object that invokes the function in regular functions.
    • Arrow functions do not have their own this; they use the this from their enclosing environment.
  6. Object Methods:
    • Object.entries() returns an array of [key, value] pairs.
  7. Scope:
    • Variables can be global, function-scoped (var), or block-scoped (let, const).
    • Closures allow persistent private state within functions.
  8. String Methods:
    • repeat(count) duplicates a string a specified number of times.