Explain how |this| value is calculated in call expression, and the difference between `foo[prop]()` and `func = foo[prop], func()`

NEW
Unassigned

Status

defect
3 years ago
2 years ago

People

(Reporter: peter.kehl, Unassigned)

Tracking

Details

User Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:46.0) Gecko/20100101 Firefox/46.0
Build ID: 20160502172042

Steps to reproduce:

// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/@@iterator

// Following works as per the above
"use strict";
var arr = ['w', 'y', 'k', 'o', 'p'];
var eArr = arr[Symbol.iterator]();
console.log(eArr.next().value); // w
console.log(eArr.next().value); // y
console.log(eArr.next().value); // k
console.log(eArr.next().value); // o
console.log(eArr.next().value); // p

// Following is a variation, which should work as per https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols. However, it fails (undefined):
var arr = ['w', 'y', 'k', 'o', 'p'];
var arrIteratorFunction = arr[Symbol.iterator];
var eArr = arrIteratorFunction();
console.log(eArr.next().value); // w
console.log(eArr.next().value); // y
console.log(eArr.next().value); // k
console.log(eArr.next().value); // o
console.log(eArr.next().value); // p


Actual results:

See above


Expected results:

Both alternatives should result in the same.
Component: Untriaged → JavaScript Engine
Product: Firefox → Core
|var eArr = arr[Symbol.iterator]();| and |var arrIteratorFunction = arr[Symbol.iterator]; var eArr = arrIteratorFunction();| are different things, in term of |this| value.

with |foo[prop]()| expression, |foo[prop]| function is called with |this == foo|
with |func = foo[prop], func()| expressions, |foo[prop]| function is called with |this == undefined|

then, Array.prototype[@@iterator] iterates over |this| value.
so, in latter case, it throws an error while converting |this| to object, as it's |undefined|.

https://tc39.github.io/ecma262/#sec-array.prototype-@@iterator
https://tc39.github.io/ecma262/#sec-array.prototype.values


So, I think this bug is basically INVALID, but can you tell me why do you think those code are same?
if there is something that can be improved/fixed in the document, it would be nice to know :)
Flags: needinfo?(peter.kehl)
Thank you for this education. I understand now and I agree with your explanation. However, I believe the current MDN doesn't explain this well. It actually even doesn't mention it. Following are my thinking steps with code example.

"use strict";
var o= {
  f: function() { return this===o; }
};

o['f'](); // true, agreed - intuitive

( o['f'] )(); // true, agreed - intuitive when seen here; however, its mechanism - where this works (that is, here) and where it stops (that is, the following example) - is not documented well. See below

var func= o['f'];
func===o['f']; // true - however, the following returns false

func(); // false, agreed (since it doesn't have 'this' object bound)- however, since func===o['f'], it's natural to expect that this should evaluate the same as:
( o['f'] )();

In other words, o['f'] has 'this' object bound, but if it is not executed as a function in the current expression (that is, it is stored in a variable or passed as a parameter to another function), then that 'this' is lost. That syntactic mechanism of 'losing' automatically bound 'this' is not documented/obvious.

Even an experienced programmer, but new to this in Javascript, will get confused. Even when following https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence - both [] and () - that is, Computed Member Access and Function Call - are left-to-right associative, and [] has a higher precedence than (). From the precedence and associativity it seems natural to assume that 'this' gets lost (forgotten), but it doesn't (if the function is evaluated within the current expression). Hence

The same goes for a dot access: o.f: it carries 'this' if the function is called within the current expression:

( o.f )()

Side note: I suggest you split expressions on multiple lines, or separate them in a way other than using | pipes |.  The pipes made it difficult to understand, especially when this seems new/not trivial.
Flags: needinfo?(peter.kehl) → needinfo?
Thank you :)

fscholz, can I have your opinion about documentation on |this| value in function call [1] with member expression [2]?
I guess, it would be nice to explain how "Reference" [3] is generated [2], how |this| is extracted from it [1][4], and how/where it's converted into a value [5] there, without stepping into it too much.

[1] https://tc39.github.io/ecma262/#sec-function-calls
[2] https://tc39.github.io/ecma262/#sec-property-accessors-runtime-semantics-evaluation
[3] https://tc39.github.io/ecma262/#sec-reference-specification-type
[4] https://tc39.github.io/ecma262/#sec-getthisvalue
[5] https://tc39.github.io/ecma262/#sec-getvalue
Component: JavaScript Engine → JavaScript
Flags: needinfo? → needinfo?(fscholz)
Product: Core → Developer Documentation
Version: 46 Branch → unspecified
Summary: array[Symbol.iterator] doesn't evaluate to an iterator function → Explain how |this| value is calculated in call expression, and the difference between `foo[prop]()` and `func = foo[prop], func()`
I guess we could add Peter's code as an example on the Array/@@iterator page how it is not working due to |this| binding.

var arr = ['w', 'y', 'k', 'o', 'p'];
var arrIteratorFunction = arr[Symbol.iterator];
var eArr = arrIteratorFunction();

And then, more general, why these two aren't the same could be and example on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this

`foo[prop]()`
`func = foo[prop], func()

I am not sure I explain the difference well enough myself in English. The spec-esque struggle is real.
Status: UNCONFIRMED → NEW
Ever confirmed: true
Flags: needinfo?(fscholz)
You need to log in before you can comment on or make changes to this bug.