Abhilash Koneri
Abhilash Koneri

Reputation: 131

How does 'this' keyword resolve in a function call

This is probably a typical question for someone new to Javascript. I did research a number of similar questions but wasn't able to explain the behavior that I am seeing.

The code below should illustrate what I am trying to do. I have 2 objects MyObjectA and MyObjectB.

MyObjectB has a method echo that simply logs a message. It prefixes the this.name to the message in an attempt to know who is executing the method. It also prints the value of this.

MyObjectA has a method called callAFunctionWithMessage which does exactly that. It accepts a message and a function and calls it.

In the global scope , the objects are instantiated and called. What I see is this.name is undefined. And the this had the value DOMWindowObject when executed in the browser and some massive infrastructure-like object when executing in nodejs. Can someone help me with some insight on this behavior? Given that MyObjectA is calling echo, i expected 'this' to point to MyObjectA.

I also, execute MyObjectB.echo('hello') where this points to to MyObjectB as expected.

function MyObjectA(name) {
    this.name=name;
}

MyObjectA.prototype = {
    name : "",
    callAFunctionWithMessage: function (msg, callback) {
        callback(msg);
    }
}

function MyObjectB(name) {
    this.name = name;
    }

MyObjectB.prototype = {
    name :"",
    echo: function(msg) {
        var messageToPrint='[from '+this.name+']'+msg;
        console.log(messageToPrint, " : " + this);
    }
}

var a = new MyObjectA("ObjA");
var b = new MyObjectB("objB");

a.callAFunctionWithMessage('hello from A!', b.echo);
// => [from result]hello from A! : [object Window]

b.echo('hello!');
// => [from objB]hello! : [object Object]

Upvotes: 1

Views: 178

Answers (3)

Bart
Bart

Reputation: 17361

When you pass b.echo as a callback you pass the function which looses it's context. There are a few simple rules to know what this will mean.

  1. In a object context it will refer to the object instance.
  2. Outside the object context it will be either the global object (Window) or undefined depending on strict mode.
  3. If strict mode is in effect this will not automatically resolve to the global object. It will be undefined.

Upvotes: 2

PSL
PSL

Reputation: 123739

When you do:

a.callAFunctionWithMessage('hello from A!', b.echo);

the function b.echo is getting passed with a global context (window in your case). so this.name is not going to be the name property of the object a but it will be of the name property of the global context, hence you see it as undefined. You can instead change it to call with the context of a itself using function.bind

 a.callAFunctionWithMessage('hello from A!', b.echo.bind(a)); //now you set the callback with the context of b itself.

In your case when inside the callback method this would represent the global scope.

fiddle

Or change the way you invoke your callback by setting a context of current, to ensure that it takes a context of no context is bound to it.

MyObjectA.prototype = {
    name : "",
    callAFunctionWithMessage: function (msg, callback) {
        callback.call(this, msg);
    }
}

Fiddle

Upvotes: 3

Paul S.
Paul S.

Reputation: 66334

The value of this will change depending on how a function is invoked, i.e. what context is it given at invoke time? The only exception to this are bound functions.

I hope the following example will aid your understanding, along with the MDN page for this

function foo() {
    console.log(this);
}

var bar = {
    'foo': foo,
    'baz': function () {foo();}
};

foo();       // -- logs the global object
             //    `foo` was called without context
bar.foo();   // -- logs `bar`
             //    `foo` was called with context `bar`
bar.baz();   // -- logs the global object
             //    `baz` (in context `bar`) called `foo` without context
var x = bar.foo;
x();         // -- logs the global object
             //    `x` was called without context, `x` is still `foo`
x.call(bar); // -- logs `bar`
             //    `x` was called with context `bar`

Upvotes: 1

Related Questions