MaximPro
MaximPro

Reputation: 536

Lost context in object

I have this code:

/// global context
function Outer(){
    /// Outer context
    this.print = function(){ 
        console.log("1: "+this) 
        inside(); /// "this" is bound to the global object. Why not bound to the Outer object?
        function inside(){ 
            console.log("2: "+this) 
        } 
   }; 
} 
function print(){
    console.log("3: "+this);
}
var obj = new Outer; 
obj.print(); /// "this" is bound to the Outer object.
print(); /// "this" is bound to the global object.

Why inside the method call, this has a global object? Can anyone explain this?

Upvotes: 2

Views: 510

Answers (6)

guramidev
guramidev

Reputation: 2238

You can understand it if you read about arrow functions new in ES6.

Until arrow functions, every new function defined its own this value (a new object in the case of a constructor, undefined in strict mode function calls, the base object if the function is called as an "object method", etc.).

Which means that every function defines it's own this and in case of a simple function it will always be a global object and not the context of a function it is defined in. Just for comparison a function which doesn't define it's own this(an arrow function):

/// global context
function Outer(){
    /// Outer context
    this.print = function(){ 
        console.log("1: "+this) 

        let inside = () => { 
            /// "this" is Outer object here
            console.log("2: "+this) 
        } 
        inside(); 
   }; 
}

Upvotes: 0

Ravindra Thorat
Ravindra Thorat

Reputation: 2002

the value of this is depends on how you are making a call to the intended function and generally leading parent object plays important role in it.

what does it mean by leading parent object?:

sampleObj.getDetails(); // here 'sampleObj' is the leading parent object for 'getDetails()' function

lets try to clear the things using some examples of making call to the function using below sample code snippet.

function globalFunction(){
    console.log('global this: ', this);
}
var simpleObj = {
  name : 'john' 
};
var complexObj = {
  outer: function(){
    console.log('outer this: ', this);
  }
}
globalFunction(); // will log global object as no leading parent object is available

complexObj.outer(); // will log 'complexObj' object as leading parent object is 'complexObj'

complexObj.outer = complexObj.outer.bind(simpleObj);
complexObj.outer(); // will log 'simpleObj' object as we have set preference as 'simpleObj' for this **

complexObj.outer.call(); // will log global object as we arent setting any preference for 'this' ***

complexObj.outer.call(simpleObj); // will log simpleObj object as we have set preference as simpleObj for 'this' ***

complexObj.outer.apply(); // will log global object as we arent setting any preference for 'this' ****

complexObj.outer.apply(simpleObj); // will log simpleObj object as we have set preference as simpleObj for 'this' ****

hopefully it will help you!

** what is bind: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

*** what is call: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call

**** what is apply: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply

Upvotes: 0

Syed Ekram Uddin
Syed Ekram Uddin

Reputation: 3091

That's because, its obeying following 2 JS rules

  • Rule 1. In Javascript, new function = new scope - that’s the rule.
  • Rule 2. When a function is called as a method of an object, this is set to the object the method is called on.

Explanation of the code:

  • For rule 2, this.print() is a feature of Outer object. So this will refer to the Outer object.

So the console.log 1 will print 1: [object Object]

  • For rule 1, the function inside() in Outer is not a feature of Outer object. So new scope will created and this assigned to window. And that's also same for the last function 'print()'

So console.log 2 will print 2: [object Window]

And console.log 3 will print 3: [object Window] as well

Hope Helps,

Run the code: https://es6console.com/j9bxeati/

Reference: https://toddmotto.com/everything-you-wanted-to-know-about-javascript-scope/#function-scope

Upvotes: 3

mashi
mashi

Reputation: 627

If it is not obvious for you, that probably means you confuse scope and context of the function. Scope is what vars function has access to during invocation. Context (this) is determined by how function is invoked.

Now look on how you invoke your function 'inside' and answer the question. Does it vividly owned by any object? That is why you have global

Upvotes: 1

Anmol Mittal
Anmol Mittal

Reputation: 853

The value of this is determined by how a function is called.

Your "inside" function was called inside "print" function, which has an object reference, but that object did not have "inside" function as a property or that object did not invoke "inside" function.

"this" refers to the object which called it, not the scope in which it is called. Hence, the global object

Upvotes: 0

Estus Flask
Estus Flask

Reputation: 222855

As the reference states,

In most cases, the value of this is determined by how a function is called. It can't be set by assignment during execution, and it may be different each time the function is called. ES5 introduced the bind method to set the value of a function's this regardless of how it's called, and ES2015 introduced arrow functions which do provide their own this binding (it remains the this value of the enclosing lexical context).

<...>

Inside a function, the value of this depends on how the function is called.

<...>

Since the following code is not in strict mode, and because the value of this is not set by the call, this will default to the global object

<...>

So, in strict mode, if this was not defined by the execution context, it remains undefined.

The posted code is not executed in strict mode, so this equals to global variable (window) inside the function when it's called like print().

If this is not the desirable behaviour, and print is expected to be called separately from the object it originally was defined on (e.g. when being passed as a callback), the function can be bound to its lexical this with arrow in ES6:

function Outer(){
    this.print = () => { ... }; 
} 

In ES5 bind or self = this recipe can be used:

function Outer(){
    this.print = (function(){ ... }).bind(this);
}

function Outer(){
    var self = this;
    this.print = function(){
      self;
      ...
    };
}

If the function isn't expected to be called with another context, it can be called with another context in-place:

print.call(this);

Or:

print.bind(this)();

Upvotes: 1

Related Questions