Brad Woods Digital Garden

Notes / JavaScript / Variables / this

The Warhammer 40k Adeptus Mechanicus symbol

Table of contents

    The word 'this'

    JavaScript - this

    Planted: 

    Status: decay

    Hits: 60

    When a function is invoked, it creates an execution context containing:

    • where the function was called (the call-stack),
    • how the function was called,
    • what parameters were passed,
    • the this reference. this is a keyword representing a binding.
    • ...

    Below, func is called in the global scope. When that is executed, this is bound to the global object. When this.a = 1 is executed, it will result in globalThis.a = 1.

    index.js

    function func() {
    this.a = 1;
    }
    func();
    console.log(func.a)
    console.log(globalThis.a)

    If the intention was to add an a property to the func object, .call(..) could be used. It calls a function and defines what this is bound to.

    index.js

    function func() {
    this.a = 1;
    }
    func.call(func);
    console.log(func.a)
    console.log(globalThis.a)
    A map of Middle Earth.

    Call-Site

    If you need to find what this is bound to at some point in your code, you 1st need to find the call-site. The location where a function is called. It can be determined by looking at the call-stack.

    index.js

    function func3() {
    // call-stack = main -> func1 -> func2 -> func3
    // call-site = func2
    }
    function func2() {
    // call-stack = main -> func1 -> func2
    // call-site = func1
    func3();
    }
    function func1() {
    // call-stack = main -> func1
    // call-site = main
    func2();
    }
    // call-stack = main
    func1();

    The call-stack can be viewed in the browser dev tools. Set a breakpoint and refresh the page, the debugger will pause at this line. It will display the current call-stack. The 2nd item from the top will be the call-site.

    A screenshot of the browser console with the call-site underlined.
    Judge Dredd

    Rules

    Base on the call-site, ask these questions in order. Stop when the 1st rule applies.

    1. Is the function called with a 'new' binding?

    index.js

    function func(a) {
    this.a = a;
    }
    const myVar = new func(1);
    console.log(myVar.a)

    When a function is invoked with new in front of it (a constructor call):

    1. 1. a new object is created
    2. 2. the object is [[Prototype]]-linked
    3. 3. the object is set as the this binding for that function call
    4. 4. unless the function returns its own alternate object, the new-invoked function call will automatically return the newly constructed object.

    2. Has 'bind(..)' been used?

    index.js

    function func1() {
    console.log(this.a);
    }
    const obj = {
    a: 1
    };
    const func2 = func1.bind(obj)
    func2()

    bind(..) returns a new function that is hard-coded to call the original function with the specified this.

    3. Is 'call(..)' or 'apply(..)' used?

    index.js

    function func() {
    console.log(this.a);
    }
    const obj = {
    a: 1
    };
    func.call(obj);

    call(..) invokes a function and defines what this should be a bound to. apply(..) would have the same result.

    4. Does the call-site have a context object?

    this is that context object.

    index.js

    function func() {
    console.log(this.a);
    }
    const obj = {
    a: 1,
    func
    };
    obj.func();

    The call-site uses obj's context to reference the function. At the point that func is called, it's preceded by an object reference to obj. This is common when using browser API functions like setTimeout(() => { .. }, 1000). setTimeout is actually window.setTimeout. Therefore, this within the anonymous function is window.

    5. 'undefined' in strict mode, global object otherwise

    The catch-all rule when none of the other rules apply.

    index.js

    function func() {
    console.log(this.a);
    }
    // A var declared in the global scope will set a property on the global object
    var a = 1;
    func();

    When func is called, this is bound to the global object.

    Wormhole Diagram

    No Bridge

    this does not refer to a function's scope.

    index.js

    function func1() {
    const a = 1;
    this.func2();
    }
    function func2() {
    console.log(this.a);
    }
    func1();

    The above code is attempting to use this to create a bridge between the scopes of func1 and func2. So func2 can access a. No such bridge is possible. You can't use a this reference to look something up in a scope.

    A JavaScript arrow function.

    Arrow-Functions

    Instead of the 4 standard binding rules, an arrow-function, () => { .. } will adopt the this binding from its enclosing function call. Set when the function is created, not to the environment in which the function is called.

    index.js

    const obj = {
    a: 1,
    func1() {
    console.log(this.a);
    },
    func2: () => {
    // As we are in an arrow function, 'this' = func2.
    // Setting func2.a = 2
    a = 2
    console.log(this.a);
    // Therefore, this.a = 2 (irrelevant where func2 gets called)
    }
    };
    // 'this' is set
    obj.func1()
    obj.func2()

    Feedback

    Have any feedback about this note or just want to comment on the state of the economy?

    Where to next?

    JavaScript
    Arrow pointing downYOU ARE HERE
    A Johnny Cab pilot