# Functions, closures, and context

# Declaration or expression?

A function declaration creates a named function in its scope. A function expression creates a function value that can be assigned, passed, or returned.

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

// Function expression
const multiply = function (a, b) {
  return a * b;
};

// Arrow-function expression
const square = value => value * value;

Declarations are useful for named operations. Expressions are useful when a function is data: for example, a callback passed to addEventListener, map, or filter. Arrow functions have lexical this and no own arguments object; they are not just shorter syntax in every situation.

# Hoisting

Before executing a scope, JavaScript initializes declarations according to their kind:

sayHello(); // works: declaration is initialized before this line
function sayHello() {
  console.log('Hello');
}

console.log(typeof oldValue); // "undefined"
var oldValue = 1;

// useValue(); // ReferenceError: temporal dead zone
const useValue = () => 2;

Function declarations can generally be called before their source line. A var binding exists early but contains undefined until assignment, so a var function expression is not callable before assignment. let and const bindings are unavailable in the temporal dead zone until initialization; function and arrow expressions are therefore not callable before their variable is initialized.

Hoisting describes initialization behavior; source code is not physically moved. Prefer defining functions before using them so control flow is easy to read.

# Closures

A closure is a function together with access to variables from its lexical environment. The inner function can keep using those variables after the outer function returns:

function createCounter() {
  let count = 0;
  return () => ++count;
}

const next = createCounter();
next(); // 1
next(); // 2

let creates a new block binding for each loop iteration, so closures can retain the correct item:

for (let number = 1; number <= 3; number += 1) {
  setTimeout(() => console.log(number), 0);
}

With var, handlers share one function-scoped binding. A factory or IIFE was a common older workaround; the let binding above is the modern equivalent.

A closure remembers lexical variables; it does not automatically snapshot every external value or DOM property.

# Context and this

Closure scope and call context are different:

  • A closure determines which lexical variables a function can access.
  • this is selected by how a regular function is called.
const user = {
  name: 'Ada',
  regular() { return this.name; },
  arrow: () => this.name
};

user.regular(); // "Ada"
user.arrow();   // does not use user as its this

An arrow function captures this from its surrounding scope and cannot define its own call context. A regular DOM event listener commonly receives the element as this, while an arrow listener keeps the surrounding this; use event.currentTarget when the element should be explicit.

Concept What it controls
let/const scope Where a binding is available
Closure Which lexical bindings a function retains
Regular-function this Context selected at the call site
Arrow-function this Lexical context captured from outside

Use regular functions when dynamic this, a constructor, or an own arguments object is needed. Use arrows for callbacks when lexical context is what you want.

# Exercises

  1. Write a function factory that returns a click handler with a private counter.
  2. Rewrite a var loop using let and explain why the handlers differ.
  3. Attach both a regular-function and an arrow-function event listener and inspect this and event.currentTarget.