Hello,
I hope you can help with explaining of what is going on with `this`
value inside the body of a generator?
Consider e.g. the following case:
// infinite objects generator
let g = new function () {
this.x = 10;
while (true) {
yield;
}
};
// generate an array of 3 objects
let objects = [1, 2, 3].map(function(i) g.next());
console.dir(objects);
Results:
[
[[Class]]: "Array",
length: 3,
0: {
[[Class]]: "Object",
x: 10
},
1: {
[[Class]]: "Object"
},
2: {
[[Class]]: "Object"
}
]
Only first object has `x` property. Also:
console.log(objects[0] == objects[1]); // false
console.log(objects[1] == objects[2]); // false
As I understand, [[Construct]] activated by the `new` calls [[Call]] of
the function, which produces the `g` generator. I look here:
http://wiki.ecmascript.org/doku.php?id=strawman:generators and see:
Calling
Let |f| be a generator function. The semantics of a function call |f(x1,
..., xn)| is:
Let E = a new VariableEnvironment record with mappings for |x1| ... |xn|
Let S = the current scope chain extended with E
Let V = a new generator object with
[[Scope]] = S
[[Code]] = f.[[Code]]
[[ExecutionContext]] = *null*
[[State]] = "newborn"
[[Handler]] = the standard generator handler
Return V
So, `g` will be the generator with the needed [[Code]] and empty
[[ExecutionContext]]. Notice, there nothing is said about `this` value.
In calling `next` (i.e. `send(undefined)`) we get into:
Internal method: send
G.[[Send]]
Let State = G.[[State]]
If State = "executing" Throw Error
If State = "closed" Throw Error
Let X be the first argument
If State = "newborn"
If X != *undefined* Throw TypeError
Let K = a new execution context as for a function call
K.currentGenerator := G
K.scopeChain := G.[[Scope]]
Push K onto the stack
Return /Execute/(G.[[Code]])
G.[[State]] := "executing"
Let Result = /Resume/(G.[[ExecutionContext]], *normal*, X)
Return Result
We see that a new context is created but again, nothing is said about
its `this` value.
When evaluating /Execute/(G.[[Code]]) we with yield get into:
Yielding
The semantics of evaluating an expression of the form |yield e| is:
Let V ?= Evaluate(e)
Let K = the current execution context
Let O = K.currentGenerator
O.[[ExecutionContext]] := K
O.[[State]] := "suspended"
Pop the current execution context
Return (*normal*, V, *null*)
Btw, what does "?=" mean?
Here the K is the context created on in the `send` method (still we
haven't any info about `this` value).
The following call to `next` will again enter `send` method with
`undefined` but we already get into:
Resuming generators
*Operation* /Resume/(K, completionType, V)
Push K onto the execution context stack
Let G = K.currentGenerator
Set the current scope chain to G.[[Scope]]
Continue executing K as if its last expression produced (completionType,
V, *null*)
where we proceed with evaluating previously saved continuation. And
again, nothing about `this` is said.
As I see, during all these steps always the same K is passed around and
evaluated. `This` value is a property of the context and K has it, but
which?
As was shown in the example above, `this` is set to the newly created
object, but it's a current SpiderMonkey's behavior; don't know how it
correlate with this draft spec.
Consider e.g. the following example (tested in SpiderMonkey):
g = new function() {
yield new Boolean((yield) == this)
};
console.log(''+ g.send(g.next())); // false
g = new function() {
yield new Boolean(this == (yield))
};
console.log(''+ g.send(g.next())); // true
Why in first call `yield` wasn't newly created object and in the second
one -- it was? What actually _should_ yield without an argument yields?
`undefined` I guess.
So don't know which behavior is correct and whether it's a strange
behavior in current SpiderMonkey.
Thanks,
Dmitry.
_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss