Tuesday, October 31, 2017

Lost context in object

Leave a Comment

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?

6 Answers

Answers 1

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

Answers 2

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)(); 

Answers 3

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

Answers 4

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

Answers 5

A function's this keyword behaves a little differently in JavaScript compared to other languages. It also has some differences between strict mode and non-strict mode.

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).

// An object can be passed as the first argument to call or apply and this will be bound to it.  var obj = {a: 'Custom'};    // This property is set on the global object  var a = 'Global';    function whatsThis(arg) {    return this.a;  // The value of this is dependent on how the function is called  }    whatsThis();          // 'Global'  whatsThis.call(obj);  // 'Custom'  whatsThis.apply(obj); // 'Custom'

Answers 6

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

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment