“this” in JavaScript

Bulicka Ola
“this” in JavaScript
7 min readJul 12, 2024
Image generated by Copilot AI

This article is dedicated to the keyword this in JavaScript. I wanted to present test cases that show the differences in what this returns. It is based solely on the documentation. The link to the documentation is here. I found it difficult to understand the mechanism at first, so with this article, I wanted to create a simple note for myself.

In the documentation it is written:

“[…] The this keyword refers to the context where a piece of code, such as a function's body, is supposed to run. Most typically, it is used in object methods, where this refers to the object that the method is attached to, thus allowing the same method to be reused on different objects.

The value of this in JavaScript depends on how a function is invoked (runtime binding), not how it is defined. When a regular function is invoked as a method of an object (obj.method()), this points to that object. When invoked as a standalone function (not attached to an object: func()), this typically refers to the global object (in non-strict mode) or undefined (in strict mode). […]”

So what this returns depends on several cases:

  1. The mode in which this is used. There are two modes: non-strict and strict.
  2. How the function is called (invocation Context), standalone function call returning this or assigned function returning this to an object’s property
  3. Function syntax, implementation in a regular function, arrow function or self-executing anonymous function.
  4. In Node.js or a browser

Test case 1:

  • Mode: non-strict
  • Invocation Context: standalone function call
  • Function: regular
  • Environment: Node.js
function functionThis() {
return this;
}

console.log(functionThis());

The functionThis function returns:

<ref *1> Object [global] {
global: [Circular *1],
clearImmediate: [Function: clearImmediate],
setImmediate: [Function: setImmediate] {
[Symbol(nodejs.util.promisify.custom)]: [Getter]
},
clearInterval: [Function: clearInterval],
clearTimeout: [Function: clearTimeout],
setInterval: [Function: setInterval],
setTimeout: [Function: setTimeout] {
[Symbol(nodejs.util.promisify.custom)]: [Getter]
},
queueMicrotask: [Function: queueMicrotask],
structuredClone: [Function: structuredClone],
atob: [Getter/Setter],
btoa: [Getter/Setter],
performance: [Getter/Setter],
fetch: [Function: fetch],
crypto: [Getter]
}

In this context, when functionThis is called without any invoking object (meaning as a standalone function), the keyword this inside this function refers to the global object. In the Node.js environment, the global object is named global.

Test case 2:

  • Mode: non-strict
  • Invocation Context: standalone function call
  • Function: regular
  • Environment: browser

In the browser, the functionThis function will also return a global object, but it will be Window.

Test case 3:

  • Mode: non-strict
  • Invocation Context: function assigned to object attribute
  • Function: regular
  • Environment: Node.js
function functionThis() {
return this;
}

const newObject = { name: "object" };

newObject.newObjectAttribute1 = functionThis;

console.log('newObject.newObjectAttribute1 ', newObject.newObjectAttribute1 );
// newObject.newObjectAttribute1 [Function: functionThis]
console.log('newObject.newObjectAttribute1() ', newObject.newObjectAttribute1() );
// newObject.newObjectAttribute1() { name: 'object', newObjectAttribute1: [Function: functionThis] }

console.log('newObject.functionThis ', newObject.functionThis );
// newObject.functionThis undefined

/*
console.log('newObject.functionThis() ', newObject.functionThis() );
TypeError: newObject.functionThis is not a function
*/

newObject.newObjectAttribute2 = functionThis();
console.log('newObject.newObjectAttribute2 ', newObject.newObjectAttribute2 );
/*
newObject.newObjectAttribute2 <ref *1> Object [global] {
global: [Circular *1],
clearImmediate: [Function: clearImmediate],
setImmediate: [Function: setImmediate] {
[Symbol(nodejs.util.promisify.custom)]: [Getter]
},
clearInterval: [Function: clearInterval],
clearTimeout: [Function: clearTimeout],
setInterval: [Function: setInterval],
setTimeout: [Function: setTimeout] {
[Symbol(nodejs.util.promisify.custom)]: [Getter]
},
queueMicrotask: [Function: queueMicrotask],
structuredClone: [Function: structuredClone],
atob: [Getter/Setter],
btoa: [Getter/Setter],
performance: [Getter/Setter],
fetch: [Function: fetch],
crypto: [Getter]
}
*/
/*
console.log('newObject.newObjectAttribute2() ', newObject.newObjectAttribute2() );
TypeError: newObject.newObjectAttribute2 is not a function
*/

In the third case, we created a regular function functionThis. Next, we created an object newObject. We assigned the function to a new property of the object as follows:
newObject.newObjectAttribute1 = functionThis;

When invoking the attribute newObject.newObjectAttribute1, we got a function object named functionThis.

When a regular function is invoked as a method of an object newObject.newObjectAttribute1(), this points to that object it is within.

In the next line, we display newObject.functionThis in the console. The notation may seem confusing: newObject.functionThis, because we have a function named functionThis. However, here we are accessing an object attribute to which nothing has been assigned, hence it returns undefined.

In the commented lines:
/*
console.log(‘newObject.functionThis() ‘, newObject.functionThis() );
TypeError: newObject.functionThis is not a function
*/

error message informs that function functionThis does not exist

Assigning it this way:
newObject.newObjectAttribute2 = functionThis();
and call as follows:
newObject.newObjectAttribute2
refers to the global object.

Test case 4:

  • Mode: strict
  • Invocation Context: standalone function call
  • Function: regular
  • Environment: Node.js

Strict mode eliminates some JavaScript silent errors by changing them to throw errors.

function functionThisUseStrict() {
"use strict"
return this;
}
console.log(functionThisUseStrict());
// undefined

This in strict mode is undefined, because it is not assigned to an object. To prevent accidental references to the global object and modifications to it.

Test case 5:

  • Mode: strict
  • Invocation Context: function assigned to object attribute
  • Function: regular
  • Environment: Node.js
function functionThisUseStrict() {
"use strict"
return this;
}
const newObject2 = { name: "object" };

newObject2.newObjectAttribute1 = functionThisUseStrict;

console.log('newObject2.newObjectAttribute1 ', newObject2.newObjectAttribute1);
// newObject2.newObjectAttribute1 [Function: functionThisUseStrict]
console.log('newObject2.newObjectAttribute1() ', newObject2.newObjectAttribute1());
/*
newObject2.newObjectAttribute1() {
name: 'object',
newObjectAttribute1: [Function: functionThisUseStrict]
}
*/
console.log('newObject2.functionThisUseStrict ', newObject2.functionThisUseStrict);
// newObject2.functionThisUseStrict undefined
/*
console.log('newObject2.functionThisUseStrict() ', newObject2.functionThisUseStrict() );
TypeError: newObject2.functionThisUseStrict is not a function
*/


newObject2.newObjectAttribute2 = functionThisUseStrict();
console.log('newObject2.newObjectAttribute2 ', newObject2.newObjectAttribute2); // newObject2.newObjectAttribute2 undefined
/*
console.log('newObject2.newObjectAttribute2() ', newObject2.newObjectAttribute2() );
TypeError: newObject2.newObjectAttribute2 is not a function
*/

Case five is similar to case three. The only difference is the addition of strict mode. However, all cases behave identically.

Test case 6 and 7:

  • Mode: non-strict tand strict
  • Invocation Context: standalone function call
  • Function: arrow
  • Environment: Node.js
const arrowFunctions = () => this;

console.log('arrowFunctions()', arrowFunctions());
// arrowFunctions {}

console.log('arrowFunctions', arrowFunctions);
// arrowFunctions [Function: arrowFunctions]


const arrowFunctionsStrict = () => {
"use strict"
return this
};

console.log('arrowFunctionsStrict()', arrowFunctionsStrict());
// arrowFunctionsStrict {}

console.log('arrowFunctionsStrict', arrowFunctionsStrict);
// arrowFunctionsStrict [Function: arrowFunctionsStrict]

In both strict and non-strict modes, arrow functions in JavaScript behave in the same way, returning object {}. This is because arrow functions do not have their own context. Instead, they inherit it from the surrounding lexical scope in which they are defined.

Test case 8 and 9:

  • Mode: non-strict and strict
  • Invocation Context: standalone function call
  • Function: arrow
  • Environment: browser

Result of arrowFunctions() and arrowFunctionsStrict()

arrowFunctions() Window {…}
arrowFunctionsStrict() Window {…}

Both arrowFunctions() and arrowFunctionsStrict() return the global object (Window in a browser environment). This happens because arrow functions do not have their own this context. They inherit this from the surrounding context in which they are defined. In the global scope, this refers to the global object (Window), regardless of whether the arrow function is in strict mode or not.

Test case 10 and 11:

  • Mode: non-strict
  • Invocation Context: function assigned to object attribute
  • Function: arrow
  • Environment: Node.js
const arrowFunctions = () => this;

const newObjectForArrowFunctions = { name: "object" };

newObjectForArrowFunctions.newObjectAttribute1 = arrowFunctions;
console.log('newObjectForArrowFunctions.newObjectAttribute1 ', newObjectForArrowFunctions.newObjectAttribute1);
//newObjectForArrowFunctions.newObjectAttribute1 [Function: arrowFunctions]
console.log('newObjectForarrowFunctions.newObjectAttribute1() ', newObjectForArrowFunctions.newObjectAttribute1());
// newObjectForArrowFunctions.newObjectAttribute1() {}

newObjectForArrowFunctions.newObjectAttribute2 = arrowFunctions();
console.log('newObjectForArrowFunctions.newObjectAttribute2 ', newObjectForArrowFunctions.newObjectAttribute2 );
// newObjectForArrowFunctions.newObjectAttribute2 {}

const arrowFunctionsStrict = () => {
"use strict"
return this
};

const newObjectForArrowFunctionsStrict = { name: "object" };

newObjectForArrowFunctionsStrict.newObjectAttribute2 = arrowFunctionsStrict;
console.log('newObjectForArrowFunctionsStrict.newObjectAttribute2 ', newObjectForArrowFunctionsStrict.newObjectAttribute2);
// newObjectForArrowFunctions.newObjectAttribute1 [Function: arrowFunctions]
console.log('newObjectForArrowFunctionsStrict.newObjectAttribute2() ', newObjectForArrowFunctionsStrict.newObjectAttribute2());
// newObjectForArrowFunctionsStrict.newObjectAttribute2() {}

newObjectForArrowFunctionsStrict.newObjectAttribute2 = arrowFunctionsStrict();
console.log('newObjectForArrowFunctionsStrict.newObjectAttribute2 ', newObjectForArrowFunctionsStrict.newObjectAttribute2 );
// newObjectForArrowFunctionsStrict.newObjectAttribute2 {}

Assigning an arrow function returning this to an object property in both strict and non-strict modes results in returning {}. Same result as for regular functions, but for a different reason. Arrow functions do not have their own this context but inherit it from the parent scope, which is called lexical scoping. In this case, it refers to the global one.

Test case 12, 13, 14 and 15:

  • Mode: non-strict and strict
  • Invocation Context: Self-Executing Anonymous Function Immediately Also known as an IIFE (Immediately Invoked Function Expression)
  • Function: regular and arrow
  • Environment: Node.js
(function () {
console.log('IIFE', this);
return this;
})();
/**
IIFE <ref *1> Object [global] {
global: [Circular *1],
clearImmediate: [Function: clearImmediate],
setImmediate: [Function: setImmediate] {
[Symbol(nodejs.util.promisify.custom)]: [Getter]
},
clearInterval: [Function: clearInterval],
clearTimeout: [Function: clearTimeout],
setInterval: [Function: setInterval],
setTimeout: [Function: setTimeout] {
[Symbol(nodejs.util.promisify.custom)]: [Getter]
},
queueMicrotask: [Function: queueMicrotask],
structuredClone: [Function: structuredClone],
atob: [Getter/Setter],
btoa: [Getter/Setter],
performance: [Getter/Setter],
fetch: [Function: fetch],
crypto: [Getter]
}
**/
(() => {
console.log('IIFE arrowFunctions', this);
return this;
})();
// IIFE arrowFunctions {}

(function () {
console.log('IIFE use strict', this);
"use strict"
return this;
})();

/*
IIFE use strict <ref *1> Object [global] {
global: [Circular *1],
clearImmediate: [Function: clearImmediate],
setImmediate: [Function: setImmediate] {
[Symbol(nodejs.util.promisify.custom)]: [Getter]
},
clearInterval: [Function: clearInterval],
clearTimeout: [Function: clearTimeout],
setInterval: [Function: setInterval],
setTimeout: [Function: setTimeout] {
[Symbol(nodejs.util.promisify.custom)]: [Getter]
},
queueMicrotask: [Function: queueMicrotask],
structuredClone: [Function: structuredClone],
atob: [Getter/Setter],
btoa: [Getter/Setter],
performance: [Getter/Setter],
fetch: [Function: fetch],
crypto: [Getter]
}
*/

(() => {
console.log('IIFE use strict arrowFunctions', this);
"use strict"
return this;
})();
// IIFE use strict arrowFunctions {}

You might have noticed, for anonymous functions in both strict and non-strict mode implemented with regular syntax, this returns the global object. For anonymous functions implemented in arrow function format in both strict and non-strict mode, this refers to an empty object {}.

Summary:

Calling a standalone arrow function or assigning it to an object property, regardless of strict or non-strict mode, returns an object {} because they do not have their own context. Instead, they inherit it from the surrounding lexical scope.

For regular functions returning this when called on their own, it usually refers to the global object (in non-strict mode) or undefined (in strict mode). In both strict and non-strict mode, when a regular function is invoked as a method of an object, this points to that object.

--

--