Why is ES6 “yield” a reserved word when called in this context?

匿名 (未验证) 提交于 2019-12-03 03:05:02

问题:

I am using node 4.1.1. When I run this code

"use strict";  function *generator() {   let numbers = [1,2,3,4,5];   numbers.map(n => yield (n + 1)); }  for (var n of generator()) {   console.log(n); } 

I get this error

  numbers.map(n => yield (n + 1));                    ^^^^^  SyntaxError: Unexpected strict mode reserved word 

If I rearrange the code to be this

"use strict";  function *generator() {   let numbers = [1,2,3,4,5];   let higherNumbers = numbers.map(n => n + 1);   for(let i=0;i<higherNumbers.length;i++) {     yield higherNumbers[i];   } }  for (var n of generator()) {   console.log(n); } 

I get the expected result.

Why does the second one work, and the first fail? And surely if a keyword is reserved, it's reserved in all contexts, not just when it's used in a arrow function?

回答1:

It is because arrow functions are not generator functions. For example,

function temp() {   yield 1; } 

Can we expect this to work? No. Because temp is not a generator function. The same is applicable to arrow functions as well.


FWIW, the usage of yield in an Arrow function is an early error as per the ECMAScript 2015 specification, as per this section,

ArrowFunction : ArrowParameters => ConciseBody

  • It is a Syntax Error if ArrowParameters Contains YieldExpression is true.

  • It is a Syntax Error if ConciseBody Contains YieldExpression is true.



回答2:

Let's first look at two examples

1. yield

function* generator(numbers) {   yield numbers.map(x => x + 1); }  for (let n of generator([1,2,3])) console.log(n); // [ 2, 3, 4 ]

Our for loop logs each value yielded by the generator. Inside our generator, we have a single yield call which will yield the result of the numbers.map call, which is a new Array. Because there is only a single yield, the only logged value is [2,3,4]

2. yield*

So yield obviously won't work in the case above. We'll have to try something else.

function* generator(numbers) {   yield* numbers.map(x => x + 1); }  for (let n of generator([1,2,3])) console.log(n); // 2 // 3 // 4

Again, our for loop logs each value yielded by the generator. Inside our generator, we yield the same result of the numbers.map call, but this time we use yield*, which yield by delegation.

What are we yielding then? Well, Array's have a built-in generator, Array.prototype[Symbol.iterator]. So at this point, the for loop is essentially directly stepping thru the generator provided by the Array. Since the array has 3 values, we see 3 logged values.


Watchful eyes

So we iterate thru numbers once using Array.prototype.map but then we iterate thru the intermediate array using the for loop? Seems like a waste doesn't it?

Let's look back at your original code though

function *generator() {   let numbers = [1,2,3,4,5];   numbers.map(n => yield (n + 1)); }  for (var n of generator()) {   console.log(n); } 

Notice that your numbers.map call is pretty meaningless. Array.prototype.map creates a new array, but your generator doesn't do anything with it. So really you're just using map to iterate thru the numbers, not because you actually care about the returned value of map


Say what you mean, mean what you say

OK, so now we know we only really care about iterating thru the numbers. So we'll use iteration the way JavaScript knows best

function* generator(numbers) {   for (let x of numbers)     yield x + 1 }  for (let n of generator([1,2,3])) console.log(n); // 2 // 3 // 4

Bingo. No tricky yield*. No double iteration. No nonsense.



回答3:

That's because the arrow function is not a generator. If I expand your arrow function, it would look something like:

function *generator() {      // <-- this is your generator function   let numbers = [1,2,3,4,5];   numbers.map(function(n){   // <-- this one isn't a generator     yield (n + 1)            // <-- there's your yield   }.bind(this)); } 


回答4:

Just discovered you can encounter this by accidentally closing your function too early.

i.e. one too many }



回答5:

[1,2,3,4,5].map(function*(v){yield v+1;}).reduce((accumulator, currentValue) => accumulator = [...accumulator].concat([...currentValue])) 

explanation...

[1,2,3,4,5].map(function*(v){yield v+1;}) 

pack all values into generator resulting

unpack into flat array

.reduce((accumulator, currentValue) => accumulator = [...accumulator].concat([...currentValue])) 

for normal use

[1,2,3,4,5].map(function*(v){yield v+1;}).forEach(v => console.log([...v][0])) 

2

3

4

5

6

[...v][0] is a bit ugly but it is works.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!