L2O class or functions

JavaScript performance comparison

Revision 4 of this test case created

Preparation code

<script>
// Function based
function noop () {}

var Enumerator1 = function (moveNext, getCurrent, dispose) {
    this.moveNext = moveNext;
    this.getCurrent = getCurrent;
    this.dispose = dispose;
};

var enumeratorCreate = Enumerator1.create = function (moveNext, getCurrent, dispose) {
    var done = false;
    dispose || (dispose = noop);
    return new Enumerator1(function () {
        if (done) {
            return false;
        }
        var result = moveNext();
        if (!result) {
            done = true;
            dispose();
        }
        return result;
    }, function () { return getCurrent(); }, function () {
        if (!done) {
            dispose();
            done = true;
        }
    });
};

function Enumerable1(getEnumerator) {
    this.getEnumerator = getEnumerator;
}

Enumerable1.prototype.map = function (selector, thisArg) {
    var parent = this;
    return new Enumerable1(function () {
        var current, index = 0, e;
        return enumeratorCreate(
            function () {
                e || (e = parent.getEnumerator());
                if (!e.moveNext()) {
                    return false;
                }
                current = selector.call(thisArg, e.getCurrent(), index++, parent);
                return true;
            },
            function () { return current; },
            function () { e.dispose(); }
        );
    });
};

Enumerable1.prototype.filter = function (selector, thisArg) {
    var parent = this;
    return new Enumerable1(function () {
        var current, index = 0, e;
        return enumeratorCreate(
            function () {
                e || (e = parent.getEnumerator());
                while (true) {
                    if (!e.moveNext()) {
                        return false;
                    }
                    var c = e.getCurrent();
                    if (selector.call(thisArg, c, index++, parent)) {
                        current = c;
                        return true;
                    }
                }
            },
            function () { return current; },
            function () { e.dispose(); }
        );
    });
};

Enumerable1.prototype.forEach = function (action, thisArg) {
    var e = this.getEnumerator(), i = 0;
    try {
        while (e.moveNext()) {
            action.call(thisArg, e.getCurrent(), i++, this);
        }
    } catch(ex) {
        throw ex;
    } finally {
        e.dispose();
    }
};  

Enumerable1.range = function (start, count) {
    return new Enumerable1(function () {
        var current = start - 1, end = start + count - 1;
        return enumeratorCreate(
            function () {
                if (current < end) {
                    current++;
                    return true;
                } else {
                    return false;
                }
            },
            function () { return current; }
        );
    });
};

// Class based
var inherits = function (child, parent) {
    function __() { this.constructor = child; }
    __.prototype = parent.prototype;
    child.prototype = new __();
};


var Enumerator2 = (function () {
    function Enumerator2() {
        this.current = null;
        this.done = false;
    }

    Enumerator2.prototype.current = null;
    Enumerator2.prototype.done = false;
    Enumerator2.prototype.moveNext = function () {
        if (this.done) {
            return false;
        }
        var result = this._moveNext();
        if (!result) {
            this.done = true;
            this.dispose();
        }
        return result;
    };

    Enumerator2.prototype.getCurrent = function () {
        return this.current;
    };

    Enumerator2.prototype.dispose = function () {
        if (!this.done) {
            this._dispose();
            this.done = true;
        }
    };

    Enumerator2.prototype._dispose = noop;
    Enumerator2.prototype._moveNext = noop;

    return Enumerator2;
}());

var Enumerable2 = (function () {
    function Enumerable2() { }

    Enumerable2.prototype.filter = function (predicate, thisArg) {
        return new FilterEnumerable2(this, predicate, thisArg);
    };

    Enumerable2.prototype.forEach = function (action, thisArg) {
        var i = 0, e = this.getEnumerator();
        try {
            while (e.moveNext()) {
                action.call(thisArg, e.getCurrent(), i++, this);
            }
        } catch (ex) {
            throw ex;
        } finally {
            e.dispose();
        }
    };

    Enumerable2.prototype.map = function (selector, thisArg) {
        return new MapEnumerable2(this, selector, thisArg);
    };

    Enumerable2.range = function (start, count) {
        return new RangeEnumerable2(start, count);
    };

    return Enumerable2;
}());

/* Map */

var MapEnumerable2 = (function (super_) {
    inherits(MapEnumerable2, super_);

    function MapEnumerable2(parent, selector, thisArg) {
        this.parent = parent;
        this.selector = selector;
        this.thisArg = thisArg;
    }

    MapEnumerable2.prototype.getEnumerator = function () {
        return new MapEnumerator2(this.parent, this.selector, this.thisArg);
    };

    return MapEnumerable2;

}(Enumerable2));

var MapEnumerator2 = (function (super_) {
    inherits(MapEnumerator2, super_);

    function MapEnumerator2(parent, selector, thisArg) {
        this.e = parent.getEnumerator();
        this.selector = selector;
        this.thisArg = thisArg;
        this.index = 0;
    }

    MapEnumerator2.prototype._moveNext = function () {
        if (!this.e.moveNext()) {
            return false;
        }
        this.current = this.selector.call(this.thisArg, this.e.getCurrent(), this.index++, this.parent);
        return true;
    };

    MapEnumerator2.prototype._dispose = function () {
        this.e.dispose();
    };

    return MapEnumerator2;
}(Enumerator2));

/* Filter */

var FilterEnumerable2 = (function (super_) {
    inherits(FilterEnumerable2, super_);

    function FilterEnumerable2(parent, predicate, thisArg) {
        this.parent = parent;
        this.predicate = predicate;
        this.thisArg = thisArg;
    }

    FilterEnumerable2.prototype.getEnumerator = function () {
        return new FilterEnumerator2(this.parent, this.predicate, this.thisArg);
    };

    return FilterEnumerable2;

}(Enumerable2));

var FilterEnumerator2 = (function (super_) {
    inherits(FilterEnumerator2, super_);

    function FilterEnumerator2(parent, predicate, thisArg) {
        this.e = parent.getEnumerator();
        this.predicate = predicate;
        this.thisArg = thisArg;
        this.index = 0;
    }

    FilterEnumerator2.prototype._moveNext = function () {
        while (true) {
             if (!this.e.moveNext()) {
                return false;
            }
            var c = this.e.getCurrent();
            if (this.predicate.call(this.thisArg, c, this.index++, this.parent)) {
                this.current = c;
                return true;
            }          
        }
    };

    FilterEnumerator2.prototype._dispose = function () {
        this.e.dispose();
    };

    return FilterEnumerator2;
}(Enumerator2));

/* Range */

var RangeEnumerable2 = (function (super_) {
    inherits(RangeEnumerable2, super_);

    function RangeEnumerable2(start, count) {
        this.start = start;
        this.count = count;
    }

    RangeEnumerable2.prototype.getEnumerator = function () {
        return new RangeEnumerator2(this.start, this.count);
    };

    return RangeEnumerable2;

}(Enumerable2));

var RangeEnumerator2 = (function (super_) {
    inherits(RangeEnumerator2, super_);

    function RangeEnumerator2(start, count) {
        this.current = start - 1;
        this.end = start + count - 1;
    }

    RangeEnumerator2.prototype._moveNext = function () {
        if (this.current < this.end) {
            this.current = this.current + 1;
            return true;
        } else {
            return false;
        }
    };

    return RangeEnumerator2;
}(Enumerator2));
</script>

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in unknown unknown
Test Ops/sec
Function based
var logs = [];
var e1 = Enumerable1
    .range(0, 1000)
    .map(function (x, i) { return x * x + i; })
    .filter(function (x) { return x % 2 === 0; })
    .forEach(function (x) {
        logs.push(x);
    });
pending…
Class based
var logs = [];
var e1 = Enumerable2
    .range(0, 1000)
    .map(function (x, i) { return x * x + i; })
    .filter(function (x) { return x % 2 === 0; })
    .forEach(function (x) {
        logs.push(x);
    });
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL. Here’s a list of current revisions for this page:

0 comments

Add a comment