javascriptiterable

Making an array-like object iterable: variable in iterator is not defined?


I'm learning to make objects iterable in Javascript.

My object is:

var arrayLikeObject = {
    0: "hello",
    1: "there",
    2: "crappy coder",
    length: 3,
}

then I do this to make it iterable:

arrayLikeObject[Symbol.iterator] = function(){
    return {
        current: 0, // <---- but... it IS defined.
        next() {
            // let current = 0; // putting it here makes it work
            if(current < this.length) {
                let a = current;
                current++;
                return {done: false, value: this[a]};
            }
            else {
                return {done: true};
            }
        }
    };
};

then when I run it with:

console.log("after making it iterable: ==============");
for(let str of arrayLikeObject) {
    console.log(str);
}

I get "current is not defined". But as far as I can see, it is defined. I don't understand. I thought functions could see variables outside their scope, but not the other way around, unless they get "overshadowed".


Solution

  • current is not a variable, it is a property, so you would need to reference it as this.current.

    However, you have another issue with this:

    In this.length and this[a], the this object is not arrayLikeObject, but the object that has the next() method.

    You can also fix this, but I think it is simpler to go the other way, and make next an arrow function. That way this.length and this[a] will work as intended. Make current a normal variable within the closure:

    const arrayLikeObject = {
        0: "hello",
        1: "there",
        2: "crappy coder",
        length: 3,
    }
    
    arrayLikeObject[Symbol.iterator] = function(){
        let current = 0;
        return {
            next: () => {
                if(current < this.length) {
                    return {done: false, value: this[current++]};
                }
                else {
                    return {done: true};
                }
            }
        };
    };
    
    console.log(...arrayLikeObject);

    Note that you don't really have to create a new function, as you can just assign Array.prototype[Symbol.iterator]:

    const arrayLikeObject = {
        0: "hello",
        1: "there",
        2: "crappy coder",
        length: 3,
    }
    
    arrayLikeObject[Symbol.iterator] = Array.prototype[Symbol.iterator];
    
    console.log(...arrayLikeObject);