1

In the following example there is a simple object. Two instances were created for me to test variable scope.

When test1a (and test2a) is assigned to the method ShowNum it behaves identically as when calling oneObj.ShowNum() by itself. However, when test1b is assigned to the method ShowNum2, then it behaves differently than calling oneObj.ShowNum2() directly.

This is a little puzzling to me because it seems that the 'this' scope is being lost during assignment, but at the same time it is NOT lost because 'num' is still found (and num is as unique to the object instance as this.num2 is).

What is the esoteric explanation for this behavior ?

function TestObject ()
{
    var num   = 25;
    this.num2 = 50;
    this.ShowNum  = function () {return num;}
    this.ShowNum2 = function () {return this.num2;}
    this.SetNum   = function (newnum) {num = newnum;}
}
var oneObj = new TestObject();
var twoObj = new TestObject(); twoObj.SetNum(100); twoObj.num2 = -12;

var test1a = oneObj.ShowNum;
var test1b = oneObj.ShowNum2;
var test2a = twoObj.ShowNum;
var test2b = twoObj.ShowNum2;

console.log(oneObj.ShowNum());
console.log(oneObj.ShowNum2());
console.log(test1a());
console.log(test1b());

console.log(twoObj.ShowNum());
console.log(twoObj.ShowNum2());
console.log(test2a());
console.log(test2b());

Result:

25
50
25
undefined

100
-12
100
undefined

EDIT: This question does seem like a variant of the one -->here as pointed out by the replies. My instinctive expectation was that var test1b = oneObj.ShowNum; should imply var test1b = oneObj.ShowNum2.bind(oneObj); making it for consistent behavior across languages (as Mahesha999 mentioned with the following)

The this keyword behaves differently in JavaScript compared to other language. In Object Oriented languages, the this keyword refers to the current instance of the class. In JavaScript the value of this is determined mostly by the invocation context of function (context.function()) and where it is called.

Right now I don't feel like pressing further and I consider this matter closed.

Thomas An
  • 473
  • 1
  • 5
  • 14
  • When you assign a property of an object to a variable, it is treated out of context. So when you do `var test1a = oneObj.ShowNum;`, `test1a` will hold the same function but will not hold the context. If you wish to hold the context, try `var test1a = oneObj.ShowNum.bind(oneObj);` – Rajesh Nov 13 '17 at 05:42
  • @Rajesh but then how come only test1b looses contest but not test1a ? – Thomas An Nov 13 '17 at 05:43
  • 3
    Unlike variables, which are bound by their lexical scope, `this` is bound at the call site. By changing how you call the function, you assign a different value to `this`. When you call `test1b()` and `test2b()`, `this` is being set to `window`. – 4castle Nov 13 '17 at 05:48
  • @4castle Why didn't the scope change for the test1a assignment (to window also)? OneObj.ShowNum() is a function too. Somehow test1a() knows which instance it came from. – Thomas An Nov 13 '17 at 05:59
  • @ThomasAn `test1a` doesn't use `this`. That's why the `this` within it didn't lose its context. Because there is no `this` there. – JLRishe Nov 13 '17 at 06:00
  • @ThomasAn variable `num` is stored as a closure variable. It is independent of `this`. – Rajesh Nov 13 '17 at 06:00
  • @ThomasAn The scope of a function never changes. The declaration of the function determines the scope once and for all. `this` is like an implicit parameter which changes depending on how you call the function. – 4castle Nov 13 '17 at 06:03
  • 1
    I don't think this is the duplicate as marked. This also involves an understanding of closures as Rajesh said – peterjb Nov 13 '17 at 06:04
  • Here's a link on [closures](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures). `num` is being captured in a closure every time the constructor is called. – peterjb Nov 13 '17 at 06:22
  • @peterjb Thanks, I was just reading that chapter a few moments ago. Do you think that test1b should also keep its closure during assignment? Wouldn't that be the more naturally expected behavior ? – Thomas An Nov 13 '17 at 06:25
  • @ThomasAn: You have edited your question to add new and somewhat narrowed down questions. However the resulting *question* is not very readable or understandable. As it is right now, I would vote for *unclear what you are asking* rather then *duplicate* - the result would be the same: close. Please rewrite your question in a manner, that from the beginning till the end, it is the *same question* - and maybe then you can also add a more fitting title. – derM Nov 13 '17 at 16:59
  • @derM, Maybe it is hazy, because I am not yet sure how to digest it. Should I see it a design inconsistency of the language, or just accept it as a quirk. In my mind var test1b = oneObj.ShowNum2; should imply var test1b = oneObj.ShowNum2.bind(oneObj); to make it consistent with other OOP language behavior. (I just don't feel like raising an issue right now). If 4castle, had his reply as an answer I would have accepted it and moved on. – Thomas An Nov 13 '17 at 18:00

1 Answers1

0

Reason :

// you are not assigning the whole object , but just passing refernce to the function
var test1a = oneObj.ShowNum;
var test1b = oneObj.ShowNum2; // so from here it will lose the context of this

test1a is like

function () {return num;}

and test1b is like

function () {return this.num2;}

Here, if you directly assign the function, here this is not pointing to parent function anymore. as you have just assigned the function

You can check that by just putting console.log inside like,

this.ShowNum2 = function () { console.log(this); return this.num2;}

Debugging : Run the below code snippet and you will get idea :

function TestObject ()
{
    var num   = 25;
    this.num2 = 50;
    this.ShowNum  = function () {return num;}
    this.ShowNum2 = function () { console.log(this); return this.num2;}
    this.SetNum   = function (newnum) {num = newnum;}
}
var oneObj = new TestObject();
var twoObj = new TestObject(); twoObj.SetNum(100); twoObj.num2 = -12;

var test1a = oneObj.ShowNum;
var test1b = oneObj.ShowNum2;
var test2a = twoObj.ShowNum;
var test2b = twoObj.ShowNum2;

console.log(oneObj.ShowNum());
console.log(oneObj.ShowNum2());
console.log(test1a());
console.log(test1b());

console.log(twoObj.ShowNum());
console.log(twoObj.ShowNum2());
console.log(test2a());
console.log(test2b());

Solution :

//.bind(parent_object);

var test1b = oneObj.ShowNum2.bind(oneObj);
var test2b = twoObj.ShowNum2.bind(twoObj);

function TestObject ()
{
    var num   = 25;
    this.num2 = 50;
    this.ShowNum  = function () {return num;}
    this.ShowNum2 = function () {return this.num2;}
    this.SetNum   = function (newnum) {num = newnum;}
}
var oneObj = new TestObject();
var twoObj = new TestObject(); twoObj.SetNum(100); twoObj.num2 = -12;

var test1a = oneObj.ShowNum;
var test1b = oneObj.ShowNum2.bind(oneObj);
var test2a = twoObj.ShowNum;
var test2b = twoObj.ShowNum2.bind(twoObj);

console.log(oneObj.ShowNum());
console.log(oneObj.ShowNum2());
console.log(test1a());
console.log(test1b());

console.log(twoObj.ShowNum());
console.log(twoObj.ShowNum2());
console.log(test2a());
console.log(test2b());
Vivek Doshi
  • 46,471
  • 9
  • 84
  • 100
  • Wait. you say "because num is defined as var and you are just accessing the global num" ... but it is not a global number. My example proved that the two instances oneObj and twoObj hold unique values of num, which tells me that num also has a concept of 'this' otherwise it wouldn't know which value to choose (25 ? or 100?) – Thomas An Nov 13 '17 at 06:04
  • @ThomasAn, sorry, its just because was going too fast for answering , please ignore that. – Vivek Doshi Nov 13 '17 at 06:05
  • quote: "Here, if you directly assign the function, here this is not pointing to parent function anymore" ... but why not ? Why should we loose closure from test1b but not from test1a during the assignment ? – Thomas An Nov 13 '17 at 06:21
  • @ThomasAn, that is because of the scope and contex of `this` , you can access `var` nested to the any level. – Vivek Doshi Nov 13 '17 at 06:24
  • ok, maybe I should sleep on it for a little because right now I am stuck thinking that 'this' should be getting its context by the closure suggested by the assignment of test1a=oneObj.ShowNum(), instead of forgetting all about it giving up later. if the naked oneObj.ShowNum(), knows what to, then so should test1a(). Again, maybe I am too tired at the moment to see a meaningful distinction. – Thomas An Nov 13 '17 at 06:36
  • @ThomasAn. yes sometimes what we all need is some sleep, and in then its just become so much clear :) – Vivek Doshi Nov 13 '17 at 06:37
  • Thanks for you suggestion of using 'bind'. This whole thing actually came up during my attempts to understand 'bind' (as well 'call', and 'apply') a couple of days ago which helped discover this glitch in my understanding of 'this' and 'closures' in Javascript. Basically this entire exercise was an indirect question of why should we have to use bind in this instance. (Turns out applying C++ habits is one of the culprits) – Thomas An Nov 13 '17 at 07:02
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/158829/discussion-between-vivek-doshi-and-thomas-an). – Vivek Doshi Nov 13 '17 at 07:09