Graduate Program KB

JavaScript Fundamentals


JavaScript Prototypes

const person = {}
person.name = "Josh"
person.age = 23
  • First we create an object literal and add properties of name and age to this object
console.log(person.name) // Josh
console.log(person.randomProperty) // Undefined
console.log(person.toString()) // '[object Object]'
console.log(person.hasOwnProperty('name')) // true
  • If we log for our property of name, as expected we get back the string "Josh"
  • Also as expected if we try and look up a property not on the object we get undefined
  • However, if we look up the toString function on the object it is not undefined among other properties
  • So the question is where do these properties come from?
const person = {} // Syntactic sugar for const person = new Object()
  • Object is a standard built in to JavaScript
  • Assigning person to an object literal is the same as invoking the Object constructor and assigning the returning object to person
const person = {
    __proto__: {} // inherited from Object.prototype 
}

// Representation of the __proto__ object inherited
{
    toString: () => {}
}
  • On every object there is a proto property that is an inherited object added to the object returned by invoking a constructor function
  • In this case from invoking the Object constructor, the Object.prototype object is referenced
  • So when we call the toString function, we are not looking it up on person, the reference is followed and looked up on Object.prototype.toString
const arr = [] // Same as const arr = new Array()
arr.filter // inherited from Array.prototype
  • Similarly after returning a new array instance from invoking the Array constructor the proto on the object returned is references the Array.prototype

Looking up a property is undefined

  • So what actually happens when we get undefined from a property lookup
arr.randomProperty // undefined

// NULL -> Object.prototype -> Array.prototype -> arr
  • First javascript will try and look up the property on the arr object and because it is not there it will look at its proto for the property.
  • This proto as we now know is a reference to Array.prototype. If it is not on the Array.prototype, this object has its own proto object.
  • This proto references the Object.prototype finally looking it up on the proto of that object. This proto is however null.
  • After following this chaining and finally recieving null the value returned by the lookup of the property is undefined

Setting the proto of an object

const animal = {
    isHuman: false
}
const dog = Object.create(animal)

console.log(dog.isHuman)
  • Using Object.create function we can set the objects proto to some other object.
  • In the above example the returned object assigned to dog has its proto set to the animal object

this

  • The purpose of the this keyword is to allow developers to reuse functions with varying contexts
  • The this keyword refers to a specific JavaScript object. However the specific object that this refers to can differ based on how a function is invoked or called

What is this?

  • To figure out what this is referencing we can use a flow diagram this-flowchart-robyn-heslop

  • To figure out what this is referencing to we must work out how this is bound

Lexical Binding

  • Is it an arrow function?
user = {
    hello = "Hi"
    greet: () => console.log(this.hello)
}
  • Looking up hello on this returns Hi
  • This is because arrow functions are lexically scoped. Instead we must look at the immediate surrounding scope.
  • That scope is the user object, so this is referencing user currently and looking up hello in the greet function will actually be looking up hello on the user object

Implicit Binding

  • Is there a value to the left side of the function call
function greet() { 
    console.log(this.hello) 
}
user = {
    hello = "Hi"
}

user.greet()
  • Greet is a stand alone function in the global scope
  • By looking to the left of the function call we can see that this will be referencing the user object for its context

Explicit Binding

  • Did we use call, apply on the function or was the function created using bind?
function greet() {
    console.log(this.hello)
}

user = {
    hello = "Hi"
}

// call and apply both invoke greet with the passed in object that this will be bound to
greet.call(user)
greet.apply(user)

// bind returns a new function that invokes greet with the user bound
const newGreet = greet.bind(user)
  • In explicit binding, you do not look to the left of the dot as there is no lookup happening (We are not using the object accessor pattern)
  • Instead using the call apply or bind functions on the Function.prototype we can bind this to some object

new Binding

  • Was the function called with the new keyword?
function User (name, age)
{
    this.name = name
    this.age = age
}
const person = new User('Josh', 23)
  • When the new keyword is called JavaScript under the hood creates a new instance of the object returned by the User constructor
  • Implicitly JavaScript also creates a this object and the passed in arguments of name and age are set as properties on this. This object is implicitly returned at the end of the function

Window Binding

  • Finally if none of the above bindings apply this is bound to the global object
  • The global object is called different names in node it is global whereas in the browser it is window
window.drink = "Coffee"

function favoriteDrink() {
    console.log(this.drink)
}

favoriteDrink()
  • In the above example this will simply reference the window object when invoked

Resources and References