Search code examples
javascriptecmascript-6for-of-loop

Modify an object's for...of loop behavior


Given the new 'Symbol' primitive in ES6, is it possible to modify/set the behavior of a for...of loop in an object?

I'm creating a little utility for 'deep extracting' values from an 'iterable' (which I define for my purposes as an Object, Array, Map or Set).

Array, Map and Set make use of for..of loops, however plain objects do not. For consistency, I'd like objects to make use of this loop (and should iterate over values rather than properties (as for...in allows you to do)).

Can this be done?


Solution

  • well, you could do something like this (usual warnings about global prototypes apply):

    Object.prototype[Symbol.iterator] = function* () {
        yield* Object.values(this);
    };
    
    x = {a: 1, b: 2}
    
    for (let y of x)
        console.log(y)

    or

    Object.defineProperty(
        Object.prototype,
        Symbol.iterator, {
            value: function*() {
                yield* Object.values(this);
            }
        }
    );
    
    myObj = {x: 1, y: 2}
    console.log([...myObj])

    However, a cleaner solution would be to introduce a generic wrapper that would enable iteration for objects that are not iterable per se:

    let iter = function* (a) {
        if (a === null || a === undefined)
            return;
    
        if (!a[Symbol.iterator]) {
            if (typeof a === 'object')
                a = Object.values(a);
            else
                a = [a];
        }
    
        for (let x of a)
            yield x;
    };
    
    console.log([...iter('abcd')])
    console.log([...iter(123)])
    console.log([...iter({a: 1, b: 2})])

    And then simply use for (x of iter(whatever)) in your code.