for
There are three forms of a for statement.
basic form
for (initialization; condition; increment)
in form
The in form enumerates the property names (keys) of an object. On each
iteration, another property name string from the object is assigned to the
variable. It is usually necessary to test object.hasOwnProperty(variable) to
determine whether the property name is truly a member of the object or was found
instead on the prototype chain.
for (myvar in obj) {
if (obj.hasOwnProperty(myvar)) {
...
}
}
of form (new in ES6)
The of for uses an object specific iterator method to loop over the "values"
of an object. What "values" means depends on the object. For arrays
this means all the values of the array, but not additional methods and
properties. See below for creating your own iterable interface to customize the
"values" for an object.
var arr = [3, 5, 7];
arr.foo = "hello";
for (var i in arr) {
console.log(i); // "0", "1", "2", "foo"
}
for (var i of arr) {
console.log(i); // "3", "5", "7"
// note that "hello" is missing
}
while
While loops have a condition and will execute as long as the condition is true. Just like other languages.
let number = 0;
while (number <= 12) {
console.log(number);
number = number + 2;
}
do
A do loop is just like a while loop except that it will always execute at least once, the first time the condition isn't tested. Ths syntax is kinda jank, the test appears AFTER the body. I guess to reflect that, but it really harms readability.
do {
yourName = prompt("Who are you?");
} while (!yourName);
console.log(yourName);
This example illustrates the one fucking time this miiight be used. If you had
just used a while loop it would fail on account of yourName not yet being
defined, but because the do loop executes once before testing you avoid that
issue.
conditional (ternary)
console.log(true ? 1 : 2); - 1
console.log(false ? 1 : 2); - 2
iterator interface
The object given to a for/of loop is expected to be iterable. This means it has
a method named with the symbol Symbol.iterator.
When called, that method should return an object that provides a second
interface, iterator. This is the actual thing that iterates. It has a next
method that returns the next value, if there is one, and a done property,
which should be true when there are no more results. Note that next, value,
and done property names are plain strings, not symbols. Only
Symbol.iterator, which is likely to be added by a lot of different objects, is
an actual symbol.
let okIterator = "OK"[Symbol.iterator]()
console.log(okIterator.next()) // { value: 'O', done: false }
console.log(okIterator.next()) // { value: 'K', done: false }
console.log(okIterator.next()) // { value: undefined, done: true }
Here's an implementation of a 2D matrix which can be iterated over:
class Matrix {
constructor(width, height, element = (x, y) => undefined) {
this.width = width
this.height = height
this.content = []
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
this.content[y * width + x] = element(x, y)
}
}
}
get(x, y) {
return this.content[y * this.width + x]
}
set(x, y, value) {
this.content[y * this.width + x] = value
}
[Symbol.iterator]() {
return new MatrixIterator(this)
}
}
class MatrixIterator {
constructor(matrix) {
this.x = 0
this.y = 0
this.matrix = matrix
}
next() {
if (this.y == this.matrix.height) return { done: true }
let value = { x: this.x, y: this.y, value: this.matrix.get(this.x, this.y) }
this.x++
if (this.x == this.matrix.width) {
this.x = 0
this.y++
}
return { value, done: false }
}
}
let matrix = new Matrix(2, 2, (x, y) => `value ${x},${y}`)
for (let { x, y, value } of matrix) {
console.log(x, y, value)
}