Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

this & Object Prototypes - Chapter 3: Symbol.iterator enumerablility #1388

Open
Beaglefoot opened this issue Dec 11, 2018 · 3 comments
Open

Comments

@Beaglefoot
Copy link

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 the Symbol as a computed property name (covered earlier in this chapter), we could have declared it directly, like var 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?

@getify
Copy link
Owner

getify commented Dec 11, 2018

This detail in the spec changed after publication of the book, which predated the spec by more than a year.

@ghost
Copy link

ghost commented Jul 13, 2019

@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.

@Beaglefoot
Copy link
Author

Beaglefoot commented Jul 13, 2019

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants