Skip to content

Instantly share code, notes, and snippets.

@Atlas7
Last active November 3, 2017 16:46
Show Gist options
  • Save Atlas7/04b411b0b9f6e945f95909d4270ec27b to your computer and use it in GitHub Desktop.
Save Atlas7/04b411b0b9f6e945f95909d4270ec27b to your computer and use it in GitHub Desktop.
JavaScript - Bind Intuition

Inspired by a lecture video by Tyler Mcginnis's http://tylermcginnis.com React Fundamentals course. on the JavaScript this keyword and the use of call and bind methods, I've created the follow example to solidify my understanding on this topic.

Some major learning:

  • call and bind are pretty much the same thing, except call is immediated invoked, whereas bind is for deferred invocation.
  • if jim.sayName method has a this in it, when we invoke jim.sayName, that this refers to jim (left of the dot).
  • if we do a jim.sayName2 = jim.sayName.bind(pete), we essentially created a new method jim.sayName2, which is identical to jim.sayName - except that the this in jim.sayname2 now refers to pete (i.e. "bind to" pete). Note that we need to assign the bind output somewhere (hence the jim.jayName2).
  • if we do a jim.sayName.call(pete) (no variable assignment is needed), we are immediately invoking jim.sayName method. The this context of this.sayName this time refers to pete. This call is immediate and does not affect the definition of the original jim.sayName. (i.e. if we do jim.sayName again, the this would still by default refers to jim, as if nothing has changed). call is handy for testing out context switching interactively.

Code

This code does the followings:

  • define a Person blue print.
  • instantiate jim and pete (based on the blue print). Each has their own devil.
  • we do a series of binds and calls to illustrate how the this context is changed.

Copy and paste this code to a jsbin interactive javascript editor, run it, and get the output in console. Play with the script to gain better understanding.

console.log("---- Defining a Person Blueprint -------")

const Person = function (name, devilName) {
  return {
    name: name,
    sayName: function() {
      console.log(`${name}: I am ${this.name}`)
    },
    devil: {
      name: devilName,
      sayName: function() {
        console.log(`Devil (of ${name}): hehehe ${this.name}`)
      },
    }
  }
}

console.log("---- Jim is born -------")

const jim = Person('jim', 'jiiimmmmy')
console.log(jim)

// { name: 'jim',
//   sayName: [Function: sayName],
//   devil: { name: 'jiiimmmmy', sayName: [Function: sayName] } }

console.log("---- This is Jim and his devil -------")

jim.sayName()          // "jim: I am jim"
jim.devil.sayName()    // "Devil (of jim): hehehe jiiimmmmy"

console.log("---- Pete is born -------")

const pete = Person('pete', 'peeeettteee')
console.log(pete)

// { name: 'pete',
//   sayName: [Function: sayName],
//   devil: { name: 'peeeettteee', sayName: [Function: sayName] } }

console.log("---- This is Pete and his devil -------")

pete.sayName()         // "pete: I am pete"
pete.devil.sayName()   // "Devil (of pete): hehehe peeeettteee"

console.log("----Jim's devil taking over Jim (with bind) -------")

jim.sayOwnDevil = jim.sayName.bind(jim.devil)
jim.sayOwnDevil()     // "jim: I am jiiimmmmy"

console.log("----Pete's devil taking over Jim (with bind) -------")

jim.sayPeteDevil = jim.sayName.bind(pete.devil)
jim.sayPeteDevil()   // "jim: I am peeeettteee"

console.log("----Jim's devil taking over Jim (with call) -------")

jim.sayName.call(jim.devil)   // "jim: I am jiiimmmmy"

console.log("----Pete's devil taking over Jim (with call) -------")

jim.sayName.call(pete.devil)  // "jim: I am peeeettteee"

console.log("----Phew... Jim is still a normal Jim -------")

jim.sayName()   // "jim: I am jim"

console.log("----Backup Normal Jim -------")

jim.sayNameBackup = jim.sayName

console.log("----Make Jim forever Jim Devil like -------")

jim.sayName = jim.sayOwnDevil
jim.sayName()   // "jim: I am jiiimmmmy"
jim.sayName()   // "jim: I am jiiimmmmy"
jim.sayName()   // "jim: I am jiiimmmmy"

console.log("----Make Jim forever Pete Devil like -------")

jim.sayName = jim.sayPeteDevil
jim.sayName()   // "jim: I am peeeettteee"
jim.sayName()   // "jim: I am peeeettteee"
jim.sayName()   // "jim: I am peeeettteee"

console.log("----Make Jim normal again (restore normal Jim backup) -------")

jim.sayName = jim.sayNameBackup
jim.sayName()   // "jim: I am jim"
jim.sayName()   // "jim: I am jim"
jim.sayName()   // "jim: I am jim"

console.log("----Inspect Jim again (now with extra devil skills) -------")

console.log(jim)

// { name: 'jim',
//   sayName: [Function: sayName],
//   devil: { name: 'jiiimmmmy', sayName: [Function: sayName] },
//   sayOwnDevil: [Function: bound sayName],
//   sayPeteDevil: [Function: bound sayName],
//   sayNameBackup: [Function: sayName] }

console.log("----Inspect Pete again (still the same pete) -------")

console.log(pete)

// { name: 'pete',
//   sayName: [Function: sayName],
//   devil: { name: 'peeeettteee', sayName: [Function: sayName] } }

Intersting note: notice that when we inspect jim towards the end, we see the following lines? (implies binding)

  • sayOwnDevil: [Function: bound sayName] - this method is the same as sayName, but the this has been bind to something else.
  • sayPeteDevil: [Function: bound sayName] - this method is the same as sayName, but the this has been bind to something else.

This is the complete jim object at the end:

// { name: 'jim',
//   sayName: [Function: sayName],
//   devil: { name: 'jiiimmmmy', sayName: [Function: sayName] },
//   sayOwnDevil: [Function: bound sayName],
//   sayPeteDevil: [Function: bound sayName],
//   sayNameBackup: [Function: sayName] }

Console Output

This is what we see in the console. Copying and pasting here to aid understanding:

---- Defining a Person Blueprint -------
---- Jim is born -------
{ name: 'jim',
  sayName: [Function: sayName],
  devil: { name: 'jiiimmmmy', sayName: [Function: sayName] } }
---- This is Jim and his devil -------
jim: I am jim
Devil (of jim): hehehe jiiimmmmy
---- Pete is born -------
{ name: 'pete',
  sayName: [Function: sayName],
  devil: { name: 'peeeettteee', sayName: [Function: sayName] } }
---- This is Pete and his devil -------
pete: I am pete
Devil (of pete): hehehe peeeettteee
----Jim's devil taking over Jim (with bind) -------
jim: I am jiiimmmmy
----Pete's devil taking over Jim (with bind) -------
jim: I am peeeettteee
----Jim's devil taking over Jim (with call) -------
jim: I am jiiimmmmy
----Pete's devil taking over Jim (with call) -------
jim: I am peeeettteee
----Phew... Jim is still a normal Jim -------
jim: I am jim
----Backup Normal Jim -------
----Make Jim forever Jim Devil like -------
jim: I am jiiimmmmy
jim: I am jiiimmmmy
jim: I am jiiimmmmy
----Make Jim forever Pete Devil like -------
jim: I am peeeettteee
jim: I am peeeettteee
jim: I am peeeettteee
----Make Jim normal again (restore normal Jim backup) -------
jim: I am jim
jim: I am jim
jim: I am jim
----Inspect Jim again (now with extra devil skills) -------
{ name: 'jim',
  sayName: [Function: sayName],
  devil: { name: 'jiiimmmmy', sayName: [Function: sayName] },
  sayOwnDevil: [Function: bound sayName],
  sayPeteDevil: [Function: bound sayName],
  sayNameBackup: [Function: sayName] }
----Inspect Pete again (still the same pete) -------
{ name: 'pete',
  sayName: [Function: sayName],
  devil: { name: 'peeeettteee', sayName: [Function: sayName] } }

Conclusion

We have illustrated how bind and call may be used to change the this context of an object's method. This article was inspired by a lecture relating to this topic at tylermcginnis.com/ (React Fundamentals).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment