var myself = {
myName: 'Manoj',
myNickNames: [ 'Minko', 'BMK' ],
printMyNickNames: function () {
'use strict';
this.myNickNames.forEach(
function (nickName) { // 1
console.log(this.myName +' is also known as '+ nickName); // 2
}
);
}
};
myself.printMyNickNames();
Uncaught TypeError: Cannot read property 'myName' of undefined
-
It did not print "Manoj is also known as Minko/BMK". As you might have expected, the
this
context to get set at the object level scope inside the function at #1
, so that it could accessmyName
viathis.myName
. But, clearly it does not. We see that thethis
is set toundefined
. Here, the function at #1
is nested within theprintMyNickNames
method of themyself
object. What we are trying here is to access thethis
context ofprintMyNickNames
(effectively themyself
object) within the function in the loop at #1
. -
Now, the function at #
1
itself has its ownthis
context, and thats why it shadows thethis
context of the methodprintMyNickNames
and also, as we are using theuse strict
within the method, thethis
context for the function at #1
is set toundefined
. On a side note, when we run the above code in the non strict mode,this
object for the function at #1
is set to the the global object i.e. thewindow
object (try running the above code after commenting theuse strict
part and see it for yourself). -
A thing to note here is because
this
is a special variable, the lexical scope chaining is not applicable tothis
. This is the biggest difference to the normal variables declared viavar
and thethis
variable. -
To overcome this problem (
punintended :) ), we need to bind the appropriate context tothis
. We can also cache thethis
variable to another temporary variable and use it to access the originalthis
object members. The following code snippets illustate this, both of these would print the appropriateconsole.log
s at #2
.// One way to bind appropriate context to this var myself = { myName: 'Manoj', myNickNames: [ 'Minko', 'BMK' ], printMyNickNames: function () { 'use strict'; this.myNickNames.forEach( function (nickName) { // 1 console.log(this.myName +' is also known as '+ nickName); // 2 - prints appropriately }.bind(this) ); } }; myself.printMyNickNames(); // Another way, using "var that = this" technique var myself = { myName: 'Manoj', myNickNames: [ 'Minko', 'BMK' ], printMyNickNames: function () { 'use strict'; var that = this; this.myNickNames.forEach( function (nickName) { // 1 console.log(that.myName +' is also known as '+ nickName); // 2 - prints appropriately } ); } }; myself.printMyNickNames();
-
Few methods also support passing of context. Here,
this
can be sent as an additional parameter to the method and the method will bind it directly.var myself = { myName: 'Manoj', myNickNames: [ 'Minko', 'BMK' ], printMyNickNames: function () { 'use strict'; this.myNickNames.forEach( function (nickName) { // 1 console.log(this.myName +' is also known as '+ nickName); // 2 - prints appropriately }, this // 2nd parameter as context. ); } }; myself.printMyNickNames();
Here is a minimalistic version of forEach function :
function forEach(array, callback, context) { var length = array.length; var i = 0; for(i; i < length; i++){ callback.apply(context, [array[i], i, array]); } }
-
Use of arrow funtions from ES6, makes it even better.
var myself = { myName: 'Manoj', myNickNames: [ 'Minko', 'BMK' ], printMyNickNames: function () { 'use strict'; this.myNickNames.forEach( nickName => console.log(this.myName + ' is also known as ' + nickName); ); } }; myself.printMyNickNames();