What is the javascript mechanism / rules that allow `p.foo = o.foo` to return a reference to a` foo` function?

I am currently learning javascript by following the You Don't Know js book.

In the This and Object Prototype section, when discussing "indirect function references", the author states

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

      

The value of the result of the assignment expression p.foo = o.foo is only referring to the underlying function object. So an efficient call site is just foo (), not p.foo () or o.foo () as you might expect. According to the rules above, the default binding rule is applied.

So it seems to be (p.foo = o.foo)

returning a function reference foo

. But what is the mechanism / rules for (p.foo = o.foo)

returning a function reference foo

? In other words, why does a simple assignment return a function reference foo

?

+3


source to share


2 answers


When I want to understand something like this, I find it helpful to break it down step by step.

  • o.foo

    looks at the object o

    and finds a property named foo

    . It returns a reference to that property, whatever it is. In this case, the property o.foo

    is a function reference foo

    .
  • p.foo = o.foo

    takes the result from above (function reference foo

    ), creates a property in an object p

    , also called foo

    . So now is p.foo

    also a function reference foo

    , exactly the same as o.foo

    .
  • This expression is enclosed in parentheses, so now you have everything to the left of the sign =

    , or p.foo

    that (as a reminder) still refers to the function foo

    .
  • We now find ()

    at the end. This calls whatever function we currently have. This is a function foo

    . Note in particular that we are not calling p.foo()

    . This will be a method call for the function for which it p.foo

    is a reference, so the this

    value will be set inside this function p

    . But we don't. We just call whatever function we return ( p.foo = o.foo )

    . As before, this is the same function foo

    , but now we have lost any connection that may have ever been with an object o

    or p

    .
  • So, when we make this call at the end, we are just calling the function foo

    without setting this

    to any particular object. Because of this, when we call the call, it is this

    set to undefined

    .
  • But we're not running in mode strict

    , so JavaScript doesn't "useful" want to give us undefined this

    , so it sets this

    to an object window

    in the browser or an object global

    in Node.
  • We used to do var a = 2;

    . So the object window

    or global

    actually has a property named a

    , and the value of that property is 2

    .
  • So when we do console.log(this.a)

    , we get a property a

    from an object window

    or global

    . This is the meaning 2

    .

What if all this code was not executed globally, but instead was inside a function? What will happen next?

function test() {
  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)(); // was 2, but now is undefined
}

test();
      

Run code


Now, when we call console.log( this.a );

inside foo

, this

it still refers to the object window

or global

. But when we set var a = 2;

, we are no longer setting the global property. We are just creating a local variable. window.a

or global.a

- undefined

(unless some other code has previously installed it).



Strict mode avoids this weirdness. If we put 'use strict';

at the beginning of the code, it will compile in strict mode. And now when we call the last function call at the end where we call the function foo

(again, not as a method!), It this

now matters undefined

instead of window

or global

. So when we try to call console.log(this.a)

, the code doesn't work because it is the this

same as undefined

, and undefined

does not (and cannot) have a property a

.

Try:

'use strict';

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)(); // was 2 in the original, but now throws an exception
      

Run code


The bottom line, at least for this particular example: Always use strict mode! Is it your friend.

+2


source


An assignment operator =

is an expression in JavaScript that produces (returns) an assigned value. Since it is an expression, it can be used wherever an expression is allowed, such as within parentheses.

For example:

let test = (a = b = c = { name: 'test' })

      

The above code will first evaluate the expression in parentheses and point to the variables c

, b

and a

to the test object (in that order), then it will point test

to the generated value from that expression. Once this line is executed, a

, b

, c

and test

will point to the same object.

Similarly,

(p.foo = o.foo)

      

Would release o.foo

backward (technically this would produce o.foo

what the function indicates foo

).



Concerning

(p.foo = o.foo)()

      

By adding an additional one ()

after parting, we will tell the engine that we want to call whatever ends the expression (p.foo = o.foo)

. Thus, we are calling the function foo

. Similar patterns are used in IIFE .

A helpful rewrite would be to think about the line above how to do it:

let produced = (p.foo = o.foo)
produced()

      

Further reading of instructions and expressions .

+1


source







All Articles