A closure is a pairing of:
- A function, and
- A reference to that function's outer scope (lexical environment)
A lexical environment is part of every execution context (stack frame) and is a map between identifiers (ie. local variable names) and values.
Every function in JavaScript maintains a reference to its outer lexical environment. This reference is used to configure the execution context created when a function is invoked. This reference enables code inside the function to "see" variables declared outside the function, regardless of when and where the function is called.
If a function was called by a function, which in turn was called by another function, then a chain of references to outer lexical environments is created. This chain is called the scope chain.
In the following code, inner
forms a closure with the lexical environment of the execution context created when foo
is invoked, closing over variable secret
:
function foo() {
const secret = Math.trunc(Math.random()*100)
return function inner() {
console.log(`The secret number is ${secret}.`)
}
}
const f = foo() // `secret` is not directly accessible from outside `foo`
f() // The only way to retrieve `secret`, is to invoke `f`
In other words: in JavaScript, functions carry a reference to a private "box of state", to which only they (and any other functions declared within the same lexical environment) have access. This box of the state is invisible to the caller of the function, delivering an excellent mechanism for data-hiding and encapsulation.
And remember: functions in JavaScript can be passed around like variables (first-class functions), meaning these pairings of functionality and state can be passed around your program: similar to how you might pass an instance of a class around in C++.
If JavaScript did not have closures, then more states would have to be passed between functions explicitly, making parameter lists longer and code noisier.
So, if you want a function to always have access to a private piece of state, you can use a closure.
...and frequently we do want to associate the state with a function. For example, in Java or C++, when you add a private instance variable and a method to a class, you are associating state with functionality.
In C and most other common languages, after a function returns, all the local variables are no longer accessible because the stack-frame is destroyed. In JavaScript, if you declare a function within another function, then the local variables of the outer function can remain accessible after returning from it. In this way, in the code above, secret
remains available to the function object inner
, after it has been returned from foo
.
Uses of Closures
Closures are useful whenever you need a private state associated with a function. This is a very common scenario - and remember: JavaScript did not have a class syntax until 2015, and it still does not have a private field syntax. Closures meet this need.
Private Instance Variables
In the following code, the function toString
closes over the details of the car.
function Car(manufacturer, model, year, color) {
return {
toString() {
return `${manufacturer} ${model} (${year}, ${color})`
}
}
}
const car = new Car('Aston Martin','V8 Vantage','2012','Quantum Silver')
console.log(car.toString())
Functional Programming
In the following code, the function inner
closes over both fn
and args
.
function curry(fn) {
const args = []
return function inner(arg) {
if(args.length === fn.length) return fn(...args)
args.push(arg)
return inner
}
}
function add(a, b) {
return a + b
}
const curriedAdd = curry(add)
console.log(curriedAdd(2)(3)()) // 5
Event-Oriented Programming
In the following code, function onClick
closes over variable BACKGROUND_COLOR
.
const $ = document.querySelector.bind(document)
const BACKGROUND_COLOR = 'rgba(200,200,242,1)'
function onClick() {
$('body').style.background = BACKGROUND_COLOR
}
$('button').addEventListener('click', onClick)
<button>Set background color</button>
Modularization
In the following example, all the implementation details are hidden inside an immediately executed function expression. The functions tick
and toString
close over the private state and functions they need to complete their work. Closures have enabled us to modularise and encapsulate our code.
let namespace = {};
(function foo(n) {
let numbers = []
function format(n) {
return Math.trunc(n)
}
function tick() {
numbers.push(Math.random() * 100)
}
function toString() {
return numbers.map(format)
}
n.counter = {
tick,
toString
}
}(namespace))
const counter = namespace.counter
counter.tick()
counter.tick()
console.log(counter.toString())
Examples
Example 1
This example shows that the local variables are not copied in the closure: the closure maintains a reference to the original variables themselves. It is as though the stack-frame stays alive in memory even after the outer function exits.
function foo() {
let x = 42
let inner = function() { console.log(x) }
x = x+1
return inner
}
var f = foo()
f() // logs 43
Example 2
In the following code, three methods log
, increment
, and update
all close over the same lexical environment.
And every time createObject
is called, a new execution context (stack frame) is created and a completely new variable x
, and a new set of functions (log
etc.) are created, that close over this new variable.
function createObject() {
let x = 42;
return {
log() { console.log(x) },
increment() { x++ },
update(value) { x = value }
}
}
const o = createObject()
o.increment()
o.log() // 43
o.update(5)
o.log() // 5
const p = createObject()
p.log() // 42
Example 3
If you are using variables declared using var
, be careful you understand which variable you are closing over. Variables declared using var
are hoisted. This is much less of a problem in modern JavaScript due to the introduction of let
and const
.
In the following code, each time around the loop, a new function inner
is created, which closes over i
. But because var i
is hoisted outside the loop, all of these inner functions close over the same variable, meaning that the final value of i
(3) is printed, three times.
function foo() {
var result = []
for (var i = 0; i < 3; i++) {
result.push(function inner() { console.log(i) } )
}
return result
}
const result = foo()
// The following will print `3`, three times...
for (var i = 0; i < 3; i++) {
result[i]()
}
Final points:
- Whenever a function is declared in JavaScript closure is created.
- Returning a
function
from inside another function is the classic example of closure, because the state inside the outer function is implicitly available to the returned inner function, even after the outer function has completed execution.
- Whenever you use
eval()
inside a function, a closure is used. The text you eval
can reference local variables of the function, and in the non-strict mode, you can even create new local variables by using eval('var foo = …')
.
- When you use
new Function(…)
(the Function constructor) inside a function, it does not close over its lexical environment: it closes over the global context instead. The new function cannot reference the local variables of the outer function.
- A closure in JavaScript is like keeping a reference (NOT a copy) to the scope at the point of function declaration, which in turn keeps a reference to its outer scope, and so on, all the way to the global object at the top of the scope chain.
- A closure is created when a function is declared; this closure is used to configure the execution context when the function is invoked.
- A new set of local variables is created every time a function is called.
Links
Best Answer
this
is a keyword in JavaScript that is a property of an execution context. Its main use is in functions and constructors. The rules forthis
are quite simple (if you stick to best practices).Technical description of
this
in the specificationThe ECMAScript standard defines
this
via the abstract operation (abbreviated AO) ResolveThisBinding:Global Environment Records, module Environment Records, and function Environment Records each have their own GetThisBinding method.
The GetThisEnvironment AO finds the current running execution context’s LexicalEnvironment and finds the closest ascendant Environment Record (by iteratively accessing their [[OuterEnv]] properties) which has a this binding (i.e. HasThisBinding returns true). This process ends in one of the three Environment Record types.
The value of
this
often depends on whether code is in strict mode.The return value of GetThisBinding reflects the value of
this
of the current execution context, so whenever a new execution context is established,this
resolves to a distinct value. This can also happen when the current execution context is modified. The following subsections list the five cases where this can happen.You can put the code samples in the AST explorer to follow along with specification details.
1. Global execution context in scripts
This is script code evaluated at the top level, e.g. directly inside a
<script>
:When in the initial global execution context of a script, evaluating
this
causes GetThisBinding to take the following steps:The [[GlobalThisValue]] property of a global Environment Record is always set to the host-defined global object, which is reachable via
globalThis
(window
on Web,global
on Node.js; Docs on MDN). Follow the steps of InitializeHostDefinedRealm to learn how the [[GlobalThisValue]] property comes to be.2. Global execution context in modules
Modules have been introduced in ECMAScript 2015.
This applies to modules, e.g. when directly inside a
<script type="module">
, as opposed to a simple<script>
.When in the initial global execution context of a module, evaluating
this
causes GetThisBinding to take the following steps:In modules, the value of
this
is alwaysundefined
in the global context. Modules are implicitly in strict mode.3. Entering eval code
There are two kinds of
eval
calls: direct and indirect. This distinction exists since the ECMAScript 5th edition.eval
call usually looks likeeval(
…);
or(eval)(
…);
(or((eval))(
…);
, etc.).1 It’s only direct if the call expression fits a narrow pattern.2eval
call involves calling the function referenceeval
in any other way. It could beeval?.(
…)
,(
…, eval)(
…)
,window.eval(
…)
,eval.call(
…,
…)
, etc. Givenconst aliasEval1 = eval; window.aliasEval2 = eval;
, it would also bealiasEval1(
…)
,aliasEval2(
…)
. Separately, givenconst originalEval = eval; window.eval = (x) => originalEval(x);
, callingeval(
…)
would also be indirect.See chuckj’s answer to “(1, eval)('this') vs eval('this') in JavaScript?” and Dmitry Soshnikov’s ECMA-262-5 in detail – Chapter 2: Strict Mode (archived) for when you might use an indirect
eval()
call.PerformEval executes the
eval
code. It creates a new declarative Environment Record as its LexicalEnvironment, which is where GetThisEnvironment gets thethis
value from.Then, if
this
appears ineval
code, the GetThisBinding method of the Environment Record found by GetThisEnvironment is called and its value returned.And the created declarative Environment Record depends on whether the
eval
call was direct or indirect:Which means:
this
value doesn’t change; it’s taken from the lexical scope that calledeval
.this
value is the global object (globalThis
).What about
new Function
? —new Function
is similar toeval
, but it doesn’t call the code immediately; it creates a function. A this binding doesn’t apply anywhere here, except when the function is called, which works normally, as explained in the next subsection.4. Entering function code
Entering function code occurs when calling a function.
There are four categories of syntax to invoke a function.
The actual function call happens at the Call AO, which is called with a thisValue determined from context; this argument is passed along in a long chain of call-related calls. Call calls the [[Call]] internal slot of the function. This calls PrepareForOrdinaryCall where a new function Environment Record is created:
In addition, there is the [[ThisValue]] field in a function Environment Record:
The NewFunctionEnvironment call also sets the function environment’s [[ThisBindingStatus]] property.
[[Call]] also calls OrdinaryCallBindThis, where the appropriate thisArgument is determined based on:
Once determined, a final call to the BindThisValue method of the newly created function Environment Record actually sets the [[ThisValue]] field to the thisArgument.
Finally, this very field is where a function Environment Record’s GetThisBinding AO gets the value for
this
from:Again, how exactly the this value is determined depends on many factors; this was just a general overview. With this technical background, let’s examine all the concrete examples.
Arrow functions
When an arrow function is evaluated, the [[ThisMode]] internal slot of the function object is set to “lexical” in OrdinaryFunctionCreate.
At OrdinaryCallBindThis, which takes a function F:
which just means that the rest of the algorithm which binds this is skipped. An arrow function does not bind its own this value.
So, what is
this
inside an arrow function, then? Looking back at ResolveThisBinding and GetThisEnvironment, the HasThisBinding method explicitly returns false.So the outer environment is looked up instead, iteratively. The process will end in one of the three environments that have a this binding.
This just means that, in arrow function bodies,
this
comes from the lexical scope of the arrow function, or in other words (from Arrow function vs function declaration / expressions: Are they equivalent / exchangeable?):Function properties
In normal functions (
function
, methods),this
is determined by how the function is called.This is where these “syntax variants” come in handy.
Consider this object containing a function:
Alternatively:
In any of the following function calls, the
this
value insidefunc
will berefObj
.1refObj.func()
refObj["func"]()
refObj?.func()
refObj.func?.()
refObj.func``
If the called function is syntactically a property of a base object, then this base will be the “reference” of the call, which, in usual cases, will be the value of
this
. This is explained by the evaluation steps linked above; for example, inrefObj.func()
(orrefObj["func"]()
), the CallMemberExpression is the entire expressionrefObj.func()
, which consists of the MemberExpressionrefObj.func
and the Arguments()
.But also,
refObj.func
andrefObj
play three roles, each:refObj.func
as a value is the callable function object; the corresponding reference is used to determine thethis
binding.The optional chaining and tagged template examples work very similarly: basically, the reference is everything before the
?.()
, before the``
, or before the()
.EvaluateCall uses IsPropertyReference of that reference to determine if it is a property of an object, syntactically. It’s trying to get the [[Base]] property of the reference (which is e.g.
refObj
, when applied torefObj.func
; orfoo.bar
when applied tofoo.bar.baz
). If it is written as a property, then GetThisValue will get this [[Base]] property and use it as the this value.Note: Getters / Setters work the same way as methods, regarding
this
. Simple properties don’t affect the execution context, e.g. here,this
is in global scope:Calls without base reference, strict mode, and
with
A call without a base reference is usually a function that isn’t called as a property. For example:
This also happens when passing or assigning methods, or using the comma operator. This is where the difference between Reference Record and Value is relevant.
Note function
j
: following the specification, you will notice thatj
can only return the function object (Value) itself, but not a Reference Record. Therefore the base referencerefObj
is lost.EvaluateCall calls Call with a thisValue of undefined here. This makes a difference in OrdinaryCallBindThis (F: the function object; thisArgument: the thisValue passed to Call):
Note: step 5 sets the actual value of
this
to the supplied thisArgument in strict mode —undefined
in this case. In “sloppy mode”, an undefined or null thisArgument results inthis
being the global this value.If IsPropertyReference returns false, then EvaluateCall takes these steps:
This is where an undefined thisValue may come from: refEnv.WithBaseObject() is always undefined, except in
with
statements. In this case, thisValue will be the binding object.There’s also
Symbol.unscopables
(Docs on MDN) to control thewith
binding behavior.To summarize, so far:
and:
Note that when evaluating
this
, it doesn’t matter where a normal function is defined..call
,.apply
,.bind
, thisArg, and primitivesAnother consequence of step 5 of OrdinaryCallBindThis, in conjunction with step 6.2 (6.b in the spec), is that a primitive this value is coerced to an object only in “sloppy” mode.
To examine this, let’s introduce another source for the this value: the three methods that override the this binding:4
Function.prototype.apply(thisArg, argArray)
Function.prototype.
{call
,bind
}(thisArg, ...args)
.bind
creates a bound function, whose this binding is set to thisArg and cannot change again..call
and.apply
call the function immediately, with the this binding set to thisArg..call
and.apply
map directly to Call, using the specified thisArg..bind
creates a bound function with BoundFunctionCreate. These have their own [[Call]] method which looks up the function object’s [[BoundThis]] internal slot.Examples of setting a custom this value:
For objects, this is the same in strict and non-strict mode.
Now, try to supply a primitive value:
In non-strict mode, primitives are coerced to their object-wrapped form. It’s the same kind of object you get when calling
Object("s")
ornew String("s")
. In strict mode, you can use primitives:Libraries make use of these methods, e.g. jQuery sets the
this
to the DOM element selected here:Constructors, classes, and
new
When calling a function as a constructor using the
new
operator, EvaluateNew calls Construct, which calls the [[Construct]] method. If the function is a base constructor (i.e. not aclass extends
…{
…}
), it sets thisArgument to a new object created from the constructor’s prototype. Properties set onthis
in the constructor will end up on the resulting instance object.this
is implicitly returned, unless you explicitly return your own non-primitive value.A
class
is a new way of creating constructor functions, introduced in ECMAScript 2015.Class definitions are implicitly in strict mode:
super
The exception to the behavior with
new
isclass extends
…{
…}
, as mentioned above. Derived classes do not immediately set their this value upon invocation; they only do so after asuper
call (happens implicitly without an ownconstructor
). Usingthis
before callingsuper
is not allowed.Calling
super
calls the super constructor with the this value of the lexical scope (the function Environment Record) of the call. GetThisValue has a special rule forsuper
calls. It uses BindThisValue to setthis
to that Environment Record.5. Evaluating class fields
Instance fields and static fields were introduced in ECMAScript 2022.
When a
class
is evaluated, ClassDefinitionEvaluation is performed, modifying the running execution context. For each ClassElement:this
refers to the class itself,this
refers to the instance.Private fields (e.g.
#x
) and methods are added to a PrivateEnvironment.Static blocks are currently a TC39 stage 3 proposal. Static blocks work the same as static fields and methods:
this
inside them refers to the class itself.Note that in methods and getters / setters,
this
works just like in normal function properties.1:
(o.f)()
is equivalent too.f()
;(f)()
is equivalent tof()
. This is explained in this 2ality article (archived). Particularly see how a ParenthesizedExpression is evaluated.2: It must be a MemberExpression, must not be a property, must have a [[ReferencedName]] of exactly "eval", and must be the %eval% intrinsic object.
3: Whenever the specification says “Let ref be the result of evaluating X.”, then X is some expression that you need to find the evaluation steps for. For example, evaluating a MemberExpression or CallExpression is the result of one of these algorithms. Some of them result in a Reference Record.
4: There are also several other native and host methods that allow providing a this value, notably
Array.prototype.map
,Array.prototype.forEach
, etc. that accept a thisArg as their second argument. Anyone can make their own methods to alterthis
like(func, thisArg) => func.bind(thisArg)
,(func, thisArg) => func.call(thisArg)
, etc. As always, MDN offers great documentation.Just for fun, test your understanding with some examples
For each code snippet, answer the question: “What is the value of
this
at the marked line? Why?”.To reveal the answers, click the gray boxes.