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
andbind
are pretty much the same thing, exceptcall
is immediated invoked, whereasbind
is for deferred invocation.- if
jim.sayName
method has athis
in it, when we invokejim.sayName
, thatthis
refers tojim
(left of the dot). - if we do a
jim.sayName2 = jim.sayName.bind(pete)
, we essentially created a new methodjim.sayName2
, which is identical tojim.sayName
- except that thethis
injim.sayname2
now refers topete
(i.e. "bind to"pete
). Note that we need to assign the bind output somewhere (hence thejim.jayName2
). - if we do a
jim.sayName.call(pete)
(no variable assignment is needed), we are immediately invokingjim.sayName
method. Thethis
context ofthis.sayName
this time refers topete
. This call is immediate and does not affect the definition of the originaljim.sayName
. (i.e. if we dojim.sayName
again, thethis
would still by default refers tojim
, as if nothing has changed).call
is handy for testing out context switching interactively.
This code does the followings:
- define a
Person
blue print. - instantiate
jim
andpete
(based on the blue print). Each has their owndevil
. - we do a series of
bind
s andcall
s to illustrate how thethis
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 assayName
, but thethis
has beenbind
to something else.sayPeteDevil: [Function: bound sayName]
- this method is the same assayName
, but thethis
has beenbind
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] }
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] } }
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).