teddcp
teddcp

Reputation: 1624

Why does 'this' result as 'Undefined' in method [javascript]

I was studying the behavior of this inside object methods and I have got stuck at one output.

Here is the code.

'use strict';

let obj, method;

obj = {
  go() { alert(this); }
};

obj.go();               // (1) [object Object]

(obj.go)();             // (2) [object Object]     QUESTION 2

(method = obj.go)();    // (3) undefined

(obj.go || obj.stop)(); // (4) undefined     ------> Doubt  <-------------------

So if the fourth one is logically equivalent to the second one, why does the logical OR cause the context to be lost?

Part-2

Please correct me if I am wrong.

  1. The evaluation of this ocurrs the way it is called/invoked with function declaration/expression.

  2. Inside arrow function, this always refers to its enclosing parent . [lexical scope]

Upvotes: 2

Views: 121

Answers (3)

KooiInc
KooiInc

Reputation: 122888

@Pointy already answered your question. Just for fun: knowing what you know now, you can get your obj.go method to run in its own scope, wrapping in an arrow function expression:

let obj, method, otherMethod, m2;

obj = {
  foo: `obj.foo here, saying: done`,
  go(txt = ``) { console.log(`obj.go here, called as: ${txt} => ${this.foo}`); }
};
(txt => obj.go(txt))(`(txt => obj.go(txt))([...])`);
(method = txt => obj.go(txt))(`(method = txt => obj.go(txt))([...])`);
(obj.go && 
  (txt => obj.go(txt)) || 
  obj.stop)(`(obj.go && txt => obj.go(txt) || obj.stop)([...])`);

// or use a wrapper (factory function)
const wrap = (...[obj, method, txtArg]) => () => obj[method](txtArg);
(otherMethod = wrap(obj, `go`, `(otherMethod = wrap(obj, \`go\`, \`[...]\`))()`))();

// or make `go` a getter and that getter a factory function
let otherObj = {
  foo: `and hithere, here's otherObj.foo`,
  get go() { return txt => 
    console.log( `otherObj.go here, called as: ${txt} => ${this.foo}` ); }
};

(m2 = otherObj.go)(`(m2 = otherObj.go)([...])`);
.as-console-wrapper { top: 0; max-height: 100% !important; }

Upvotes: 1

Scott Marcus
Scott Marcus

Reputation: 65808

Actually, #'s 3 and 4 return [object Window]

See comments:

let obj, method;

obj = {
  go() { alert(this); }
};

obj.go();               // [object Object] represents obj

(obj.go)();             // [object Object] represents obj

(method = obj.go)();    // [object Window] because the go method is not running in the context of obj

(obj.go || obj.stop)(); //  Same as above

Upvotes: 0

Pointy
Pointy

Reputation: 413682

In case 2, the parentheses do not change the fact that the function reference value on the left side of () came from an object property reference. Thus the function invocation happens in exactly the same way as in case 1, with the object reference bound to this.

In case 4, however, the || evaluation inside those parentheses cause that relationship to be lost; all that's left is the function reference without any related object. That's because the logic of || doesn't care about the fact that the function reference came from an object property lookup, so it's result value is just the function reference "naked".

As to your follow-on questions, neither assertions are worded correctly. The value of this does depend on how a function is invoked, but that has nothing to do with where the function came from or how it was declared. Inside arrow functions, the value of this is not a reference to its lexical parent, it's the same as the value of this in the lexical parent.

Upvotes: 1

Related Questions