Background and PurposeFor those unaccustomed to Ruby, Ruby supports range literals i.e. 1..4 meaning 1, 2, 3, 4 and 1...4 meaning 1, 2, 3.  This makes a Ruby for loop pretty sweet:for i in 1..3    doSomething(i)endIt works for non-numeric types and that's pretty cool but out of our scope.A similarly convenient thing exists in Python, range, with some additional features and drawbacks.  range(1, 4) evaluates to [1, 2, 3].  You can also provide a step parameter via range(0, 8, 2) which evaluates to [0, 2, 4, 6].  There is no option to make the last element inclusive (as far as I know).  range calculates its elements the moment it is invoked, but there is also a generator version to avoid unnecessary object creation.  In combination with Python's list construction style, you can do all sorts of cool stuff like [x*x for x in range(0, 8, 2)], which is right on the border of this question's scope.Now that ES6 has generators and the for-of statement (and most platforms support them), iteration in JavaScript is quite elegant; there is generally only one truly right (or at least best) way to write the loop for your task.  However, if you are iterating over a series of numbers, ES6 offers nothing new.  That's not just in comparison to ES5, but to pretty much every curly-bracket-based language before it.  Python and Ruby (probably other languages I don't know, too) have proven that we can do it better and eliminate stroke-inducing code like:while (i--) {    sillyMistake(array[i--]);}for (    var sameThing = youHaveWrittenOut;    verbatimFor >= decades;    thinkingAboutDetails = !anymore) {    neverInspectAboveForErrors();    assumeLoopVariantIsNotModifiedInHere();    if (modifyLoopVariant()) {        quietlyScrewUp() || ensureLoopCondition() && causeInfiniteLoop();    }}ES6 for-of, spread operator, and this generator can eliminate all manual loop counter fiddling, unreadable three-part for, and smelly loops that initialize arrays.  Please explain how I have succeeded or failed to do so, including any use cases this doesn't cover.Code for Reviewconst sign = Math.sign;/** * Returns true if arguments are in ascending or descending order, or if all * three arguments are equal.  Importantly returns false if only two arguments * are equal. */function ord(a, b, c) {  return sign(a - b) == sign(b - c);}/** * Three overloads: * *  range(start, end, step) *    yields start, start + step, start + step*2, ..., start + step*n *    where start + step*(n + 1) would equal or pass end. *    if the sign of step would cause the output to be infinite, e.g. *    range(0, 2, -1), range(1, 2, 0), nothing is produced. * *  range(start, end) *    as above, with step implicitly +1 if end > start or -1 if start > end * *  range(end) *    as above, with start implicitly 0 * *  In all cases, end is excluded from the output. *  In all other cases, start is included as the first element *  For details of how generators and iterators work, see the ES6 standard */function* range(start, end, step) {  if (end === undefined) {    [start, end, step] = [0, start, sign(start)];  } else if (step === undefined) {    step = sign(end - start) || 1;  }  if (!ord(start, start + step, end)) return;  var i = start;  do {    yield i;    i += step;  } while (ord(start, i, end));}/*  Use Cases.  Feedback on output method is not necessary.  Insights on use  cases themselves are welcome.*/(function shortFor(ol) {  for (var i of range(0, 16, 2)) ol.append($(<li>).text(i.toString()));})($(#short-for .vals));(function spreadMap(ol) {  for (var el of [...range(4)].map(x => x * x)) {    ol.append($(<li>).text(el.toString()));  }})($(#spread-map .vals));(function correspondingIndex(ol) {  var a = [1, 2, 3],    b = [4, 5, 6];  for (var i of range(a.length)) a[i] += b[i];  for (var el of a) ol.append($(<li>).text(el.toString()));})($(#corresponding-index .vals));<!--   jQuery is included here to conveniently emit results of test cases;  the code to be reviewed has no dependencies.  Feedback on HTML is not necessary.--><script src=https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js></script><body>  <div class=use-case id=short-for>    <h1>Use Case: Less verbose simple for loop</h1>    <code>for (var i of range(0, 16, 2))</code>    <p>Values of i:      <ol class=vals></ol>  </div>  <div class=use-case id=spread-map>    <h1>Use Case: Initialize an array without push()</h1>    <code>[...range(4)].map(x => x*x)</code>    <p>Array contents:      <ol class=vals></ol>  </div>  <div class=use-case id=corresponding-index>    <h1>Use Case: Add corresponding elements of two arrays</h1>    <code>      var a = [1, 2, 3], b = [4, 5, 6];      for (var i of range(a.length)) a[i] += b[i];    </code>    <p>Contents of array a:      <ol class=vals></ol>  </div></body>
Range iterator in ES6 similar to Python and Ruby for
javascript;iterator;ecmascript 6;generator
Answers propose replacingyield i;i += step;withyield start + i*step;++i;The differences are related to floating point accuracy and performance.  In my original design, the iterator would endlessly spit out the initial value if the step value was too small in comparison, literally bigNumber + smallNumber === bigNumber.  In addition, if the increment was not perfectly represented as a float (e.g. 1/3), error would pile up on the output after many iterations.  By incrementing an integer i and multiplying by step instead, the iterator would eventually halt and always yield the most appropriate float.The reason I did not do this initially is that I had integers in mind; while all numbers in JS are supposed to be of interchangeable real number type, it is theoretically possible for a JIT or something to optimize into integer addition if it was always used that way.  Integer math is way faster than floating point math, and addition is much faster than multiplying for either type.  Math between a float and an integer is actually the worst, since conversion eats time too.After some testing, I have concluded that only very few cases (e.g. asm.js style for caller & callee) will allow that optimization to occur and translate into a tangible performance difference, and on many platforms it can just as easily be prevented by using yield and for (... of ...) or overshadowed by call and return time.  Given time, ES6 implementations may evolve to do better at this, and a special fast integer range iterator will be justified, but for the moment, the suggestion of multiplying the step instead of repeatedly adding it is sound.Finally, the original code fails to produce number sequences of a single element because it returns early if the second element will be beyond end.  The intent of the early return was to make the iterator empty if step was the wrong sign.  I tried to combine that idea with invalidating the iterator if you gave it start equal to end, for e.g. not iterating the indexes of an empty array or string by using ord, but that doesn't work.  It's better to spell out both conditions, and the final correct code is:const sign = Math.sign;function ord(a, b, c) {  return sign(a - b) == sign(b - c);}function* range(start, end, step) {  if (end === undefined) {    [start, end, step] = [0, start, sign(start)];  } else if (step === undefined) {    step = sign(end - start) || 1;  } else if (sign(end - start) != sign(step)) {    return;  }  if (start === end) return;  var i = 0, result;  do {    result = start + i*step;    yield result;    ++i;  } while (ord(start, start + i*step, end));}See above for comments.