Iterable applies to values
A value can be iterable or not. It needs to implement the well known symbol @@iterator or @@asyncIterator. The methods implemented must also fulfil the iterable protocol by returning an iterator. With those in place, there are special interactions that can be done with such values by treating them as something that can be iterated over (hence the "iterable" name). Here are some examples:
The most basic and perhaps most common use for iterables is to iterate over them. The for...of loop will do just that and take items from an iterator until there are none left.
String:
const str = "hello world";
for (const char of str)
console.log(char);
.as-console-wrapper { max-height: 100% !important; }
Array
const arr = ["a", "b", "c", "d"];
for (const item of arr)
console.log(item);
.as-console-wrapper { max-height: 100% !important; }
Custom object
const iterable = {
[Symbol.iterator]() {
let repeat = 0;
return {
next() {
return {
value: 42,
done: repeat++ >= 3
};
}
}
}
}
for (const item of iterable)
console.log(item);
.as-console-wrapper { max-height: 100% !important; }
When spreading values, the iterator is used and you get something for each value that comes from that iterator. For example spreading into an array [...value] will create an array with all values. Spreading into a function call fn(...value) will call the function with each item as an argument.
String
const str = "hello world";
console.log([...str]); //spread into array
console.log(...str); //spread into function call
.as-console-wrapper { max-height: 100% !important; }
Array
const arr = ["a", "b", "c", "d"];
console.log([...arr]); //spread into array
console.log(...arr); //spread into function call
.as-console-wrapper { max-height: 100% !important; }
Custom object
const iterable = {
[Symbol.iterator]() {
let repeat = 0;
return {
next() {
return {
value: 42,
done: repeat++ >= 3
};
}
}
}
}
console.log([...iterable]); //spread into array
console.log(...iterable); //spread into function call
.as-console-wrapper { max-height: 100% !important; }
The name might be slightly misleading. Array destructuring always uses the iterator of an object. It does not mean it can only be used on arrays.
String
const str = "hello world";
const [first, second] = str;
console.log(first, second);
.as-console-wrapper { max-height: 100% !important; }
Array
const arr = ["a", "b", "c", "d"];
const [first, second] = arr;
console.log(first, second);
.as-console-wrapper { max-height: 100% !important; }
Custom object
const iterable = {
[Symbol.iterator]() {
let repeat = 0;
return {
next() {
return {
value: 42,
done: repeat++ >= 3
};
}
}
}
}
const [first, second] = iterable;
console.log(first, second);
.as-console-wrapper { max-height: 100% !important; }
Enumerable is for object properties
Only object properties can be enumerable. Not any value. This can be configured by using Object.defineProperty() or Object.defineProperties() or Reflect.defineProperty() or Object.create().
Non-enumerable object properties
It is hard to get an exhaustive list but this conveys the idea - non-enumerable properties are excluded from some "bulk" operations on properties.
However, non-enumerable properties are still accessible directly. They are not "hidden" or "private", just do not show up with the most common mechanisms to grab all properties.
const obj = Object.defineProperties({}, {
"a": { value: 1, enumerable: true},
"b": { value: 2, enumerable: false},
"c": { value: 3, enumerable: true},
});
for (const prop in obj)
console.log("for...in:", prop); //a, c
console.log("Object.keys():", Object.keys(obj)); // [ "a", "c" ]
console.log("Object.values():", Object.values(obj)); // [ 1, 3 ]
const clone1 = {...obj};
console.log("clone1:", clone1); // { "a": 1, "c": 3 }
console.log('"b" in clone1:', "b" in clone1); // false
console.log("clone1.b:", clone1.b); // undefined
const clone2 = Object.assign({}, obj);
console.log("clone2:", clone2); // { "a": 1, "c": 3 }
console.log('"b" in clone2:', "b" in clone2); // false
console.log("clone2.b:", clone2.b); // undefined
//still accessible
console.log('"b" in obj:', "b" in obj); // true
console.log("obj.b:", obj.b); // 2
.as-console-wrapper { max-height: 100% !important; }
There are also mechanisms that allow seeing the non-enumerable properties: Object.getOwnPropertyNames() and Object.getOwnPropertyDescriptors() for example will be able to show them.