DEV Community

Cover image for JavaScript execution context (this)
Naga Chaitanya Konada
Naga Chaitanya Konada

Posted on

2 1

JavaScript execution context (this)

There is a YouTube playlist that I did explaining the whole concept mentioned in this article, if you are that person who wants to watch and learn, please head on there.

The agenda

  • Talk about the execution context
  • About use strict and global this
  • Where to define a function
    • Does location matter for functions
    • Putting a function in an object literal
    • Inside a method function
  • How to invoke a function
    • Normal function invocation
    • Method invocation
    • Explicit binding invocation
  • How arrow functions differ
    • Where to declare the arrow functions
    • How to invoke them
  • Conclusion
    • Recap differences between using use effect and not
    • Different types of invocations
    • Location of a normal function
    • Arrow functions invocation and location

What is this

The this keyword refers to the object that a function gets based on how it is invoked. For arrow functions, it refers to the this context that gets assigned to the enclosing function.

depends on

  1. whether you used use strict
  2. how the function is invoked
  3. where the function is declared
  4. whether it is an arrow function or now

About use strict and this

When you use this in global scope, it refers to the window object in a browser. It refers to globalThis when in Node.js environment.

But if you use strict mode (by putting use strict at the beginning of your file), then you will not get window object when you use this. In fact it points to undefined.

function foo() {
    console.log(this === window); // true
}
Enter fullscreen mode Exit fullscreen mode
"use strict";
function foo() {
    console.log(this === window); // false
}
Enter fullscreen mode Exit fullscreen mode

Where to define a function

In modern JavaScript development, we generally tend to put functions in their own files, thanks to the JavaScrpt ES6 modules, CommonJS pattern and many other techniques that work towards using per-file concept.

But we are not touching the module system or the import and export feature of ES6. In this series, we are only concerned about the question of whether a function is declared outside another function or not.

function foo() {
    console.log(this === obj);
}

const obj = {
    name: "naga",
    foo: function() {
        console.log(this === obj);
    }
}

obj.foo(); // true
foo(); // false
Enter fullscreen mode Exit fullscreen mode

Remove duplicate function declaration:

function foo() {
    console.log(this === obj);
}

const obj = {
    name: "naga",
    foo: foo
}

obj.foo(); // true
foo(); // false
Enter fullscreen mode Exit fullscreen mode

Location does not matter when it comes to using the this context:

// foo.js
export default function foo() {
    console.log(this);
}

// bar.js
import foo from './foo.js';

const obj = {
    name: "naga",
    foo: foo
}

obj.foo(); // prints obj
foo(); // prints window
Enter fullscreen mode Exit fullscreen mode

invoking a member method without the object

const obj = {
    name: "naga",
    foo: function () {
        console.log(this === obj);
    }
}

obj.foo(); // true

const foo = obj.foo;
foo(); // false
Enter fullscreen mode Exit fullscreen mode

Putting a function inside a method


const obj = {
    name: "naga",
    foo: function() {
        function bar() {
            console.log(this === obj);
        }
        console.log(this === obj);
        return bar;
    }
}

const barFunc = obj.foo(); // true
barFunc(); // false
Enter fullscreen mode Exit fullscreen mode

Ways to invoke a function

normal invocation

function foo() {
    console.log(this); // global or window
}

foo();
Enter fullscreen mode Exit fullscreen mode

method invocation

function foo() {
    console.log(this); // points to obj
}

const obj = {
    foo: foo
}

obj.foo(); // prints obj
Enter fullscreen mode Exit fullscreen mode

explicit binding

function foo() {
    console.log(this); // normally prints global or window
}

const obj = {
    bar: 10
}

const boundFoo = foo.bind(obj);

boundFoo(); // prints obj coz of the binding
Enter fullscreen mode Exit fullscreen mode

using call or apply

function foo() {
    console.log(this); // normally prints global or window
}

const obj = {
    bar: 10
}

foo.call(obj); // prints obj coz of the binding
foo.apply(obj); // prints obj coz of the binding
Enter fullscreen mode Exit fullscreen mode

call vs apply

const math = {
    add: function () {
        const args = Array.from(arguments);
        return args.reduce((sum, num) => sum + num);
    }
}

const thisArg = null;
const add5 = math.add.bind(thisArg, 5); // returns a curried function

console.log(add5(10)); // 15
console.log(math.add.call(thisArg, 5, 10)); // 15
console.log(math.add.apply(thisArg, [5, 10])); // 15

Enter fullscreen mode Exit fullscreen mode

Fixing sub-function problem

the problem

const obj = {
    name: "naga",
    foo: function() {
        function bar() {
            console.log(this === obj);
        }
        console.log(this === obj);
        return bar;
    }
}

const barFunc = obj.foo(); // true
barFunc(); // false
Enter fullscreen mode Exit fullscreen mode

using scope

const obj = {
    name: "naga",
    foo: function() {
        const self = this;
        function bar() {
            console.log(self === obj); // oh yeah works
        }
        console.log(this === obj); // always true
        return bar;
    }
}

const barFunc = obj.foo(); // true
barFunc(); // true
Enter fullscreen mode Exit fullscreen mode

using explicit binding

const obj = {
    name: "naga",
    foo: function() {
        function bar() {
            console.log(this === obj);
        }
        console.log(this === obj);
        return bar;
    }
}

const barFunc = obj.foo(); // true
const barFuncBound = barFunc.bind(obj);
barFuncBound(); // now it works --> true
Enter fullscreen mode Exit fullscreen mode

How arrow functions differ from normal functions regarding this

We know normal functions take the this context based on how they are invoked and not based on where they are declared*.*

Arrow functions take the this context based on where they are declared and not based on how they are invoked.

const foo = () => {
    console.log(this === window); // true
}

foo(); // true

const obj = {
    foo: foo
};
obj.foo(); // true, so not bound to obj even though it is a method

const objFooBound = obj.foo.bind(obj);
objFooBound(); // true, still points to window, bind fails

const fooBound = foo.bind(obj);
fooBound(); // still true, bind fails
Enter fullscreen mode Exit fullscreen mode

What if we declare in a function

Now the arrow function totally obeys the enclosing scope's this context because it is declared inside it.

function foo() {
    const bar = () => {
        console.log(this === window);
    }
    bar();
}

foo(); // true, enclosing function is called in the normal way

const obj = {
    baz: 10,
    foo: foo
}
obj.foo(); // now false, enclosing function called using method invocation

const boundFoo = foo.bind({});
boundFoo(); // now also false, enclosing function bound to an object
Enter fullscreen mode Exit fullscreen mode

visiting our old example

const obj = {
    name: "naga",
    foo: function() {
        const bar = () => {
            console.log(this === obj); // true, now it takes context of the foo method 
        }
        console.log(this === obj); // obviously true
        return bar;
    }
}

const bar = obj.foo(); // true
bar(); // true
Enter fullscreen mode Exit fullscreen mode

this fixes the problem of having functions inside methods of an object. you may use arrow functions.

Conclusion

  • Declare normal functions anywhere, just not inside the object methods
  • Use arrow functions for functions inside methods
  • You can invoke normal functions in three ways: normal way, as an object method and by explicitly binding
  • Arrow functions do not care how you invoke them, all they care is where they are declared.
  • Use use strict to avoid accidentally putting stuff in the global context (window or globalThis)

Image of AssemblyAI tool

Challenge Submission: SpeechCraft - AI-Powered Speech Analysis for Better Communication

SpeechCraft is an advanced real-time speech analytics platform that transforms spoken words into actionable insights. Using cutting-edge AI technology from AssemblyAI, it provides instant transcription while analyzing multiple dimensions of speech performance.

Read full post

Top comments (0)

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

👋 Kindness is contagious

Explore a sea of insights with this enlightening post, highly esteemed within the nurturing DEV Community. Coders of all stripes are invited to participate and contribute to our shared knowledge.

Expressing gratitude with a simple "thank you" can make a big impact. Leave your thanks in the comments!

On DEV, exchanging ideas smooths our way and strengthens our community bonds. Found this useful? A quick note of thanks to the author can mean a lot.

Okay