4

Here is an example where o.foo(); is 3 but (p.foo = o.foo)(); is 2?

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

var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };

o.foo(); // 3
(p.foo = o.foo)(); // 2”

If I do something like this then I get 4 which is what I want. How are those 2 examples are different?

p.foo = o.foo;
p.foo();  // 4
Callat
  • 2,928
  • 5
  • 30
  • 47
Alexey Tseitlin
  • 1,031
  • 4
  • 14
  • 33
  • Do you mean `o.foo()` returns `{a: 3, foo: ƒ}` ? – Ulad Kasach Apr 17 '18 at 19:58
  • @UladKasach sorry ment `console.log( this.a );` edited my code – Alexey Tseitlin Apr 17 '18 at 19:58
  • 1
    That's because you are using a self-invoking function. Take a look [here](https://stackoverflow.com/questions/592396/what-is-the-purpose-of-a-self-executing-function-in-javascript) – Alexandre Miziara Apr 17 '18 at 20:00
  • @AlexandreMiziara - Are you sure this is an IIFE? And if it is, how does that explain the result? – Oliver Charlesworth Apr 17 '18 at 20:01
  • 1
    When you do p.foo = o.foo. You are trying to assign two unequal objects (one has more properties than the other). Then on top of that you call () at the end which commands the function to invoke immediately. Naturally that would cause some issues – Callat Apr 17 '18 at 20:05
  • @OliverCharlesworth, I believe it is. A self-invoking function runs automatically/immediately when you create it, so `foo` will print the value of the variable `a` that was already defined on the scope. – Alexandre Miziara Apr 17 '18 at 20:05
  • @AlexandreMiziara - But (a) the function isn't being created there, and (b) why would that affect the scoping rules? – Oliver Charlesworth Apr 17 '18 at 20:08
  • @AlexandreMiziara that is not what people generally mean by IIFE. It's a function invocation, but the left-hand side is not a function definition expression. However, that's just a pedantic opinion-based usage note and I invite you to use the term however you like :) – Pointy Apr 17 '18 at 20:10
  • @Pointy I'm sorry I haven't cleary saw that. The left side is not an anonymous function, so according to the [docs](https://developer.mozilla.org/en-US/docs/Glossary/IIFE) that really isn't a IIFE. Thanks for your explanation. – Alexandre Miziara Apr 17 '18 at 20:15
  • @OliverCharlesworth so what I said is most likely not the cause of the problem – Alexandre Miziara Apr 17 '18 at 20:17

3 Answers3

6

This :

(p.foo = o.foo)();

Is pretty much the same as doing this:

d = (p.foo = o.foo);
d();

Basically what it says is that the return of an assignment is the function itself in the global context. On which a is 2.

Julien Grégoire
  • 16,864
  • 4
  • 32
  • 57
4

Performing that assignment operation before the function call here:

(p.foo = o.foo)();

causes the object reference to p to be lost. If you instead wrote

p.foo = o.foo;
p.foo();

you'd get 4. As it is, you get 2 because the value of this will be window in the function.

Because that parenthesized subexpression is an assignment result, there's no object lookup directly associated with the result of the expression — the value of the assignment expression is just the function reference without a contextual object. Thus the runtime doesn't have a value to use for this when it calls the function, so this is bound by default to window (or the global object in whatever context).

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • hold on I think that explanation is wrong ... – Pointy Apr 17 '18 at 20:04
  • "*The ( ) creates a subexpression "layer" that causes the object association to be dropped.*" - It must be more complicated than that, though. `(o.foo)();` works fine. – Oliver Charlesworth Apr 17 '18 at 20:04
  • @OliverCharlesworth yes that's what I realized because a dim memory was making me think I'd definitely used parentheses in cases vaguely (but not exactly) like this; see edit. – Pointy Apr 17 '18 at 20:06
  • I find that kind of odd. Why is it that p gets lost instead of o? Isn't o the r_value operand? – Callat Apr 17 '18 at 20:06
  • @Callat see edit; the original explanation in the last paragraph was me typing before thinking clearly. – Pointy Apr 17 '18 at 20:07
4

actually your code is wrong because your snippet doesnt run, you should be doing the following: console.log( this.a );

and now, lets see how this works.

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

this will call this in the scope of the caller, so lets investigate our results.

so, you are setting a=2 globally, and then in the objects o.a = 3 and p.a=4

so:

calling o.foo it will return 3 because this is pointing to o

calling p.foo it will return 4 because this is pointing to p

BUT

calling (p.foo = o.foo)(); it will return 2 because it is not pointing to any object, so it will take your scope (which is the global scope) and then it will return 2.

if you go with:

p.foo = o.foo
p.foo() //4

it will return 4 successfully because it is pointing to p.

Prince Hernandez
  • 3,623
  • 1
  • 10
  • 19