Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you make properties enumerable but not iterable in javascript?

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

like image 657
Jim Jones Avatar asked Jul 22 '14 01:07

Jim Jones


People also ask

What is the difference between enumerable and iterable in JavaScript?

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

What is enumerable properties in JavaScript?

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.

What is not iterable in JavaScript?

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.

How do you make a property non enumerable?

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.


2 Answers

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.

like image 188
Andres Avatar answered Oct 30 '22 12:10

Andres


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 of obj.

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)

like image 34
Bergi Avatar answered Oct 30 '22 14:10

Bergi