Graduate Program KB

Objects Orientated in JavaScript


this

  • Robyn's BEAST Article
  • this references the execution context.
    • JavaScript Interpreter begins execution -> Global Execution Context
    • When a function is invoked -> Function Execution Context
  • A this-aware function can have a different context each time it's called, which makes it more flexible & reusable.
  • Essentially the this keyword is for providing something with context, which makes it unique for a given context.
  • The this keyword can't be set how you'd think in an arrow function. It will just behave like a normal variable.
var teacher = "Kyle";

function ask(question) {
    console.log(this.teacher, question);
}

function otherClass() {
    var myContext = {
        teacher: "Suzy"
    };
    ask.call(myContext, "Why?"); // Suzy Why?
}

otherClass()

Global Context

  • globally this will reference the window in browser and global in node.
  • You can always access the global object using globalThis.

Function Context

  • When a function is invoked it creates a new function context.
  • The value of this will depends on how the function is invoked.
    • See below mental model.
  • If we invoke a function the following way someObject.function(), this will be bound to whatever is on the left of the . operator.
const boundGreetMeAncientOne = greetMe.bind(user1); // Returns a function with 'this' explicitly bound to the 'user1' object

Using new

  • When we use the new operator the function's this is then bound to a new empty object.
  • What new actually does:
    1. Creates a brand new empty object
    2. Links to that object to another object
    3. Call function with this set to the new object
    4. If function does not return an object, assume the return of this.
var favouriteThings = ["Sadness","Goths"];

function listMyFavouriteThings(myFaves) {
    this.favouriteThings = myFaves;
    console.log(this.favouriteThings);
}

// Invoking `listMyFavouriteThings` function using the `new` operator
// `this` === `{}` (new, empty object)
new listMyFavouriteThings( ["JavaScript", "Linux", "Pastries"]); //Logs: ["JavaScript", "Linux", "Pastries"]
console.log(this.favouriteThings) // Logs: ["Sadness","Goths"];

// Invoking `listMyFavouriteThings` function without the `new` operator
// `this` === `globalThis`
listMyFavouriteThings(["JavaScript", "Linux", "Pastries"]); //Logs: ["JavaScript", "Linux", "Pastries"]
console.log(this.favouriteThings) //Logs: ["JavaScript", "Linux", "Pastries"]

Useful Mental Models

  • What is this?

    • Is it an arrow function?
      • Lexical Binding
    • Is there a value to the left hand side of the function call?
      • Implicit Binding
    • Is the function invoked using call or apply?
      • Explicit Binding
    • Was the function created using bind?
      • Explicit Binding
    • Was the function called with the new operator?
      • new binding (empty object)
    • Window Binding

Determination on a function call / order of precedence

  1. Is the function called by new
  2. Is the function called by call() or apply()
    • bind() essentially uses apply()
  3. Is the function called on a context object?
  4. DEFAULT: to the global object (unless in strict mode)

CAUTION: Objects are not a Scope, this can trip you up when dealing with arrow functions in objects.

  • Take the example below, the this keyword will just behave as a normal variable in an arrow function, when it looks for a variable it won't see the one in the object, that is not a scope... it will look in global where there isn't anything, hence undefined.
var workshop = {
    teacher: "Kyle",
    ask: (question) => {
        console.log(this.teacher, question);
    },
};

workshop.ask("What happened to 'this'?"); // undefined What happened to 'this'?
workshop.ask.call(workshop, "Still no 'this'?"); // undefined Still no 'this'?
  • ONLY use arrow functions when you need lexical this
  • NOTE: When creating an object class you only need to hard bind methods that use the this keyword
{
    function0() {
        arr.sort(this.function1.bind(this))
        arr.Map(this.function2)
    }
    function1(){
        return this.variable
    },
    function2() {
        return 3 + 5
    }
}
  • Key thing to notice is when using these methods, I only had to hard bind the method that actually used a this reference inside of it.
  • In other words, we only need to hard bind this-aware functions.

Bindings:

  • Implicit Binding: workshop.ask("Why?");, calling a method from an object (or NamespaceType, etc)
  • Dynamic Binding: can be called the same as above, but the value will be determined at runtime as it can be dynamically changed for different contexts.
  • Explicit Binding: function.call(context, argumentToPassIn)
  • Hard Binding: workshop.ask.bind(workshop)
  • Default Binding: workshop(), just a basic function call, will throw a TypeError if function needs a this input for context.
  • This point is demonstrated here:
var teacher = "Kyle";

function ask(question) {
    console.log(this.teacher, question);
}

function askAgain(question) {
    "use strict";
    console.log(this.teacher, question);
}

ask("Why?"); // Kyle Why?
askAgain("Why again?") // TypeError

class

  • The class feature looks like so:
class Workshop {
    constructor (teacher) {
        this.teacher = teacher;
    }
    ask(question) {
        console.log(this.teacher, question);
    }
}
  • If you have a child class, you can refer to a method in the parent with the super keyword
  • This is also known as relative polymorphism.
class AnotherWorkshop extends Workshop {
    ask(msg) {
        super.ask(msg.toUpperCase())
    }
}

Prototypes

  • A "constructor call" makes an object linked to its own prototype.
  • When a class instance is created, it has a link with the class at creation but after created they are separate things.
    • Think of relationship between house and house plan (architect design), if you remove a wall on the design, the built house won't lose a wall will it?
  • Prototype class, under syntactic sugar:
    function Workshop(teacher) {
        this.teacher = teacher;
    }
    Workshop.prototype.ask = function(question) {
        console.log(this.teacher, question);
    }
    var deepJS = new Workshop("Kyle");
    var reactJS = new Workshop("Dempsey");
    deepJS.ask("Why?"); // Kyle Why?
    reactJS.ask("Why?"); // Dempsey Why?
    
  • Looking at the above code, when we call ask what JS is going to do is that it will first check the respective objects (deepJS and reactJS) for an ask method... it doesn't have one. So we then check up the prototype chain and find the ask method in Workshop's prototype.
  • You can link objects together like so:
function Workshop(teacher) {
    this.teacher = teacher;
}
Workshop.prototype.ask = function(question) {
    console.log(this.teacher, question);
}
function AnotherWorkshop(teacher) {
    Workshop.call(this, teacher);
}
AnotherWorkshop.prototype = Object.create(Workshop.prototype); // Creates the link to the Workshop object.
// If the above line wasn't there, AnotherWorkshop prototype wouldn't be linked to Workshop, it'd just be linked to Object.prototype
AnotherWorkshop.prototype.speakUp = function(msg){
    this.ask(msg.toUpperCase());
};
var newJS = new AnotherWorkshop("Kyle");
newJS.speakUp("this is wild"); // Kyle THIS IS WILD
  • This linkage happens through the prototype linkage chain, which is a really effective way to link objects... via their prototypes.
  • A big takeaway to know what this is referring to, LOOK AT THE CALL SITE.

Inheritance vs Behavior Delegation (OO vs OLOO)

  • Delegation is the pattern of design for what we have just been talking about with prototypes, it's different to classes.

    • JavaScripts prototype system is a delegation system, not a class system.
    • You can implements a class in a prototype system, but you can't do prototypes in a class based system.
  • OLOO Objects Linked to Other Objects

    • Delegated objects looks like the following:
    var Workshop = {
        setTeacher(teacher) {
            this.teacher = teacher;
        },
        ask(question) {
            console.log(this.teacher, question);
        }
    };
    var AnotherWorkshop = Object.assign(
        Object.create(Workshop),
        {
            speakUp(msg) {
                this.ask(msg.toUpperCase());
            }
        }
    );
    
    var JSRecentParts = Object.create(AnotherWorkshop);
    JSRecentParts.setTeacher("Kyle");
    JSRecentParts.speakUp("But isn't this cleaner?");
    // Kyle BUT ISN"T THIS CLEANER?
    
    • Object.create and Object.assign are the magic parts here to link the objects together.

Return