Number.prototype.times

Anyone who has ever worked with the PrototypeJS library will recognise this little beauty. It’s a gem of an addition and something that I firmly believe should be added to the ECMAScript standard. It allows a function to be called a set number of times, such as the following example:

var n = 4;
// This will alert 0, then 1, 2 and finally 3:
n.times(function (i) {
    alert(i);
});
// Worth noting that the following code would do the same:
// n.times(alert);

I think this is far more graceful than using a for or while loop, especially because no new variables are created.

The PrototypeJS guys were smart enough to allow the function to be called with a specific context, so you could do something like this:

var n = 4;
// This will alert "Hello 0" then "Hello 1" etc.
n.times(function (i) {
    alert(this + ' ' + i);
}, 'Hello');

This sort of functionality is something that I could find a use for in my scripts, but I don’t want to bring in the PrototypeJS library each time. Luckily, the code to create this method is very straight forward. I’ve added one or two things to it so it feels more like a standard ECMAScript method.

(function () {

    'use strict';

// The toInteger function attempts to convert any object to an integer (rather
// than a float) and simulates the ToInteger algorithm described in the
// ECMAScript specs.
// http://es5.github.com/#x9.4
    function toInteger(o) {
        var n = +o,
            r = n;
        if (isNaN(n)) {
            r = 0;
        } else if (n !== 0 && isFinite(n)) {
            r = (n < 0 ? -1 : 1) * Math.floor(Math.abs(n));
        }
        return r;
    }

// The isFunction function simply detects whether or not the given object is a
// function.
    function isFunction(o) {
        return Object.prototype.toString.call(o) === '[object Function]';
    }

// Always wrap prototype additions to native objects in an if statement. If
// Number.prototype.times is ever standardised, the in-built browser method
// will be much faster than this method and may work differently.
    if (!Number.prototype.times) {
        Number.prototype.times = function (iterator, context) {

            var i = 0,
// Start by forcing "this" to be a positive integer.
                t = Math.abs(toInteger(this));

// Check to ensure that iterator is a function. Throw an Error if it is not.
            if (!isFunction(iterator)) {
                throw new TypeError(iterator + ' is not a function');
            }

// Execute iterator t times in the context that the user has supplied (or
// undefined if the user did not define a context). Pass the current number to
// iterator on each execution.
            while (i < t) {
                iterator.call(context, i);
                i += 1;
            }

// Return the integer that was derived from the "this" keyword, not the
// original context.
            return t;
        };
    }
}());

If you want to play with this method, I've created a fiddle with a very basic example.

There is one gotcha with this method that will catch out anyone not expecting it:

4.times(alert); // Syntax Error

This isn't the implimentation, it's JavaScript. You see, JavaScript automatically inserts 0s to make complete numbers, no doubt something that was designed to make coding easier:

var foo = .5; // JavaScript turns this into 0.5

Sadly, this means that the dot after the 4 in the Syntax Error example has a 0 automatically added after it, so JavaScript interprets is like this:

4.0times(alert);

Obviously that should be a Syntax Error because of the missing dot. It also means that this is perfectly valid code:

4..times(alert); // alerts 0, 1, 2, 3
// Interpreted as:
// 4.0.times(alert);

To get around that, PrototypeJS recommended that number literals were wrapped in parentheses to avoid the confusion:

(4).times(alert); // alerts 0, 1, 2, 3

Of course, not using a literal like my first example would also avoid any syntactical confusion.

There are differences between my implimentation and the PrototypeJS version

I run the context through a toInteger function whereas PrototypeJS takes a slightly different tactic. It means that PrototypeJS would interpret decimals differently, the following code will cause 4 alerts with my implimentation and 5 with the PrototypeJS one:

(4.1).times(alert);

I will also return the integer wheras PrototypeJS returns the original context. In the above example, PrototypeJS would return 4.1 whereas my interpretation would return 4.

Finally, my implimentation forces the context to be a positive integer, so the following code will cause 4 alerts to appear with my version, but none in PrototypeJS:

(-4).times(alert);

I believe my version is the more logical and the more in-keeping with ECMAScript's other methods, I mention the differences so not to catch out anyone who simply picks up my version and assumes the two match perfectly.

You're free to use my version in any project you have, I hope it's useful.

Leave a Reply

Your email address will not be published. Required fields are marked *