You-Dont-Know-JS
You-Dont-Know-JS copied to clipboard
this & Object Prototypes - Chapter 3: Symbol.iterator enumerablility
In the end of the chapter there's an example and statement:
var myObject = {
a: 2,
b: 3
};
Object.defineProperty( myObject, Symbol.iterator, {
enumerable: false,
writable: false,
configurable: true,
value: function() {
var o = this;
var idx = 0;
var ks = Object.keys( o );
return {
next: function() {
return {
value: o[ks[idx++]],
done: (idx > ks.length)
};
}
};
}
} );
// iterate `myObject` manually
var it = myObject[Symbol.iterator]();
it.next(); // { value:2, done:false }
it.next(); // { value:3, done:false }
it.next(); // { value:undefined, done:true }
// iterate `myObject` with `for..of`
for (var v of myObject) {
console.log( v );
}
// 2
// 3
Note: We used
Object.defineProperty(..)
to define our custom@@iterator
(mostly so we could make it non-enumerable), but using theSymbol
as a computed property name (covered earlier in this chapter), we could have declared it directly, likevar myObject = { a:2, b:3, [Symbol.iterator]: function(){ /* .. */ } }
.
I'm somewhat confused by the part:
(mostly so we could make it non-enumerable)
MDN states that Symbol.iterator
is non-enumerable, my own experimentation is a little bit confusing...
const iterable = {
a: 1,
[Symbol.iterator]: function() {}
};
for (x in iterable) console.log(x); // a
console.log(iterable.propertyIsEnumerable(Symbol.iterator)); // true ...WTF?!
Is there something else we should know?
This detail in the spec changed after publication of the book, which predated the spec by more than a year.
@Beaglefoot Did you get the answer?
According to MDN, Symbol.iterator
is non-enumerable. It is correct absolutely. Note that Symbol.iterator
is also property of Symbol
:
Object.getOwnPropertyDescriptor(Symbol, 'iterator');
// {value: Symbol(Symbol.iterator), writable: false, enumerable: false, configurable: false}
You misunderstood its meaning. That doesn't mean that Symbol.iterator
property of any object is also non-enumerable by default. Instead, its enumerable
can be different by how to define it. Let's check:
var object = {
[Symbol.iterator]: function() {}
};
Object.getOwnPropertyDescriptor(object, Symbol.iterator);
// {value: ƒ, writable: true, enumerable: true, configurable: true}
object.propertyIsEnumerable(Symbol.iterator); // true (of course).
// What about to change its `enumerable`?
Object.defineProperty(object, Symbol.iterator, {
enumerable: false
});
object.propertyIsEnumerable(Symbol.iterator); // false
It is helpful to think that an object uses Symbol.iterator
property which is property of Symbol
as its own property of the your object.
But, why the Symbol.iterator
doesn't appear in for...in
loop? That's because a symbol doesn't appear in the for...in
loop. You can check it from MDN here:
A for...in loop only iterates over enumerable, non-Symbol properties...
Object.defineProperty(object, Symbol.iterator, {
enumerable: true
});
object.propertyIsEnumerable(Symbol.iterator); // true
for (let prop in object) {
console.log(prop);
} // nothing printed.
Upd: seems I am a little bit late with an answer... But here it is anyway.
@jinbeomhong Ok, I may misinterpret this, by here's my explanation.
First of all, by default enumerable
property descriptor is set to true
({ a: 1 }).propertyIsEnumerable('a'); // true
The same is true for user defined symbol properties.
const mySym = Symbol('js is weird');
({ [mySym]: 1 }).propertyIsEnumerable(mySym); // true
Arrays, strings and other built-in iterables have predefined Symbol.iterator
property, whose enumerable property descriptor is set to false.
([]).propertyIsEnumerable(Symbol.iterator); // false
('').propertyIsEnumerable(Symbol.iterator); // false
But if you try to redefine this property, then default property descriptor rules will take place.
var arr = [];
arr[Symbol.iterator] = () => {};
(arr).propertyIsEnumerable(Symbol.iterator); // true
The second part is actual traverse over properties. And spec says:
next method iterates over all the String-valued keys
So that's why we don't see these enumerable properties in for...in
loop.