I was just wondering if there is a way to make an object property enumerable like in a for in
loop but not show up in a for of loop
sort of like
Object.defineProperty({},'prop',{
enumerable:true,
iterable:false
}
if not, are there any plans to implement a feature like this? Or does the for of
loop use the enumerable property
"Enumerable" refers to enumerating things, which can mean the same as iterating, but only if the language doesn't already use "iterable" for that purpose. If a language has both, "enumerable" almost certainly means something else, probably something more powerful.
Enumerable properties are those properties whose internal enumerable flag is set to true, which is the default for properties created via simple assignment or via a property initializer. Properties defined via Object. defineProperty and such are not enumerable by default.
The JavaScript exception "is not iterable" occurs when the value which is given as the right-hand side of for...of , as argument of a function such as Promise. all or TypedArray. from , or as the right-hand side of an array destructuring assignment, is not an iterable object.
To create a non-enumerable property we have to use Object. defineProperty() method. This a special method to create non-enumerable properties in an object. In the following example, three properties such as name, age and country were created normally and a property named "salary" was created using Object.
I did some digging around at the Mozilla Development Network (MDN).
Turns out that objects have a obj.propertyIsEnumerable(prop)
method to check if the property is enumerable. From the examples given in the MDN, properties inherited through the prototype chain are not enumerable, thus the method returns false for those properties.
An example of non enumerable properties are constructors and the property length
of arrays.
For the iterable part of the question, I will quote MDN: "Iterable in ECMAScript 6 is an interface (or in other words, a protocol), not a type of object like Array or Map".
That is to say, the object needs to implement this interface to make its properties iterable. This is the case for built in iterables such as String, Array, Map, Set, WeakSet and Generator objects.
The following code illustrated this:
var aString = "hello"
typeof aString[Symbol.iterator] // "function"
aString[Symbol.iterator]() + "" // "[object String Iterator]"
[...aString] // ["h", "e", "l", "l", "o"]
Of course, you can define your own iterator implementation.
Coming back to the question, properties attached to the object or its prototype (directly, meaning not via inheritance) will be displayed as enumerable in the for...in
loop. For the iterables you need an implementation, either the ones mentioned before, or your own. Here is a great example from MDN.
let arr = [ 3, 5, 7 ];
arr.foo = "hello";
for (let i in arr) {
console.log(i); // logs "0", "1", "2", "foo" these are property names or indexes
}
for (let i of arr) {
console.log(i); // logs "3", "5", "7" these are actual values of an
// iterable implementation provided by the array prototype
}
The let
keyword is equivalent in this context to a var
definition (though it has more implications but they are outside the scope of this post).
As you can see the array has an implementation already of the iterable
interface (from the array prototype), thus it yields its values when using the for...of
loop, whereas the foo
property is not displayed (neither value nor property name).
So if your object does not implement an iterable
interface, it should not be iterable (in principle) and thus it will display its properties' in a for...of
loop.
The difference between for in
and for of
evaluations is that one uses obj.[[enumerate]]
and the other uses GetIterator(obj)
to determine the looped items. [[enumerate]]
does (unless obj
is a Proxy)
return an Iterator object whose
next
method iterates over all the String-valued keys of enumerable properties ofobj
.
GetIterator
will (try to) call the @@iterate
method - however, normal objects don't implement this interface.
So no property will show up in for of
loops unless you explicitly state it:
function defineIterableProperty(obj, prop, desc) {
let old = obj[Symbol.iterate];
Object.defineProperty(obj, prop, desc);
obj[Symbol.iterate] = function* () {
if (old) yield* old.call(this);
yield prop;
};
return obj;
}
(Untested, but I hope you get the idea)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With