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
?
source to share
When I want to understand something like this, I find it helpful to break it down step by step.
-
o.foo
looks at the objecto
and finds a property namedfoo
. It returns a reference to that property, whatever it is. In this case, the propertyo.foo
is a function referencefoo
. -
p.foo = o.foo
takes the result from above (function referencefoo
), creates a property in an objectp
, also calledfoo
. So now isp.foo
also a function referencefoo
, exactly the same aso.foo
. - This expression is enclosed in parentheses, so now you have everything to the left of the sign
=
, orp.foo
that (as a reminder) still refers to the functionfoo
. - We now find
()
at the end. This calls whatever function we currently have. This is a functionfoo
. Note in particular that we are not callingp.foo()
. This will be a method call for the function for which itp.foo
is a reference, so thethis
value will be set inside this functionp
. But we don't. We just call whatever function we return( p.foo = o.foo )
. As before, this is the same functionfoo
, but now we have lost any connection that may have ever been with an objecto
orp
. - So, when we make this call at the end, we are just calling the function
foo
without settingthis
to any particular object. Because of this, when we call the call, it isthis
set toundefined
. - But we're not running in mode
strict
, so JavaScript doesn't "useful" want to give us undefinedthis
, so it setsthis
to an objectwindow
in the browser or an objectglobal
in Node. - We used to do
var a = 2;
. So the objectwindow
orglobal
actually has a property nameda
, and the value of that property is2
. - So when we do
console.log(this.a)
, we get a propertya
from an objectwindow
orglobal
. This is the meaning2
.
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();
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
The bottom line, at least for this particular example: Always use strict mode! Is it your friend.
source to share
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()
source to share