Lazy lists with transformations, using generators & iterators I built this very-basic lazy list (I'll add more methods as I need them). You provide it an array, a generator or any iterator. It creates a lazy list, which lets you run a pipeline of transformations in a lazy manner, meaning that they'll only be applied as you pull values out of the list.

Here's the class:

class List {
    static range(start, end) {
        return new List(function* () {
            while (start &lt;= end) {
                yield start++;
            }
        });
    }

    constructor(source) {
        if (typeof source == 'function') {
            this.generator = source;
        } else {
            this.generator = function* () {
                yield* source;
            };
        }
    }

    filter(predicate) {
        return new List(function* () {
            for (const item of this) {
                if (predicate(item)) {
                    yield item;
                }
            }
        }.bind(this));
    }

    map(mapper) {
        return new List(function* () {
            for (const item of this) {
                yield mapper(item);
            }
        }.bind(this));
    }

    take(size) {
        return new List(function* () {
            for (const item of this) {
                if (size--) {
                    yield item;
                } else {
                    break;
                }
            }
        }.bind(this));
    }

    *[Symbol.iterator] () {
        yield* this.generator();
    }

    toArray() {
        return [...this];
    }
}


Here's a simple example of how to use it:

List.range(1, 10 ** 9)
    .filter(number =&gt; number % 10 == 0)
    .map(number =&gt; 'Item ' + number)
    .take(5)
    .toArray();


Trying the same with a regular array:

Array.from({ length: 10 ** 9 }, (v, i) =&gt; i + 1)
     .filter(number =&gt; number % 10 == 0)
     .map(number =&gt; 'Item ' + number)
     .slice(0, 5);


...will run out of memory before it completes.
I can not find fault with your code apart from the use of class of which i am not a fan. 

So I will just present an alternative syntax that gives a little extra encapsulated protection. Its usage is slightly different. List is instantiated via a factory and closure holds the generator list. I also freeze each instance of List to further protect the state. 

It also does not have to mess about with const list = this which from your side-note comment is a bit of an annoyance.

const lazyList = (() =&gt; {

    const range = (start, end) =&gt; List(function* () {
       while(start &lt;= end) { yield start++ }
    });    
    function List(source) {
        var list;
        if (typeof source === "function") { list = source }
        else { list = function* () { yield* source } }

        return Object.freeze({
            filter(pred){
                return List(function* () {
                    for (const item of list()) { pred(item) ? yield item : 0 }
                });
            },
            map(map) {
                return List(function* () {
                    for (const item of list()) { yield map(item) }
                });
            },
            take(count) {
                return List(function* () {
                    for (const item of list()) {
                        if (count--) { yield item }
                        else { break }
                    }
                });
            },
            toArray() { return [...list()] },
            *[Symbol.iterator] () { yield* list() }
        });
    } 
    return Object.freeze({range, List});

})();