3

I know it's a matter of taste, but for my taste using for loop each time I want to iterate over array is bad. So I came up with this:

Array.prototype.each = function(callback) {
  for (var i = 0; i < this.length; i++)
    callback(this[i]);
}

Now I can do:

[10, 20, 30].each(function(n) { console.log(n/10) })

Later I found some tips on the Internet that suggested this approach, but I still wonder if it's free from side effects. It seems very obvious, and that is what worries me :)

I'm not using any library like jQuery or Prototype. I'm coding for Node.js.

ThinkingStiff
  • 64,767
  • 30
  • 146
  • 239
Tad Lispy
  • 2,806
  • 3
  • 30
  • 31
  • 6
    If you're using Node.js, the function is already available! It's called "forEach", not "each". It's part of JavaScript. – Pointy Apr 29 '12 at 12:45

3 Answers3

6

I suggest you use forEach. It's implemented in most modern browsers, and the shim is provided by MDN.

On node.js, it's implemented (it uses V8, same engine as Chrome), so you don't need a shim.

Its use is something like the following:

arr.forEach( function( el ) {
    // play with "el"
} )

Also, I suggest you look at the new array methods provided by ES5:

Florian Margaine
  • 58,730
  • 15
  • 91
  • 116
  • 1
    Thanks. So - can I use it always instead of for(var i = 0; [...] to iterate over array? Most examples on SO tend to use for loop. – Tad Lispy Apr 29 '12 at 13:14
  • Yes, you can. Most examples on SO suck :p Also, the `for` loop is *slightly* faster, but `forEach` is just so much better that the *slight* performance loss is worth it. – Florian Margaine Apr 29 '12 at 13:15
3

This does have a pretty big side effect

If we now run the following code

a = [10, 20, 30];
for (var i in a) {
    console.log(i + ": " + a[i]);
}

And check the console output, we see this:

0: 10
1: 20
2: 30
each: function (callback) {
  for (var i = 0; i < this.length; i++)
    callback(this[i]);
}

Which restricts the type of for loop you can do on an array to

a = [10, 20, 30];
for (var i = 0; i < a.length; ++i) {
    console.log(a[i] + ": " + a[i]);
}
Andrew Brock
  • 1,374
  • 8
  • 13
  • The native `.forEach()` callback function is passed the index as its second parameter. That's one of the reasons to use the real built-in version of the function: some thought went into its design :-) – Pointy Apr 29 '12 at 12:54
  • I wouldn't feel comfortable looping arrays with `for-in`, even if I *didn't* extend Array.prototype. Besides indices-as-strings and iteration of non-own properties, the semantics of Objects and Arrays JS are already so blurred as to cause confusion, without making the problem worse like this. – greim Feb 04 '14 at 00:41
1

From:

// Production steps of ECMA-262, Edition 5, 15.4.4.18
// Reference: http://es5.github.io/#x15.4.4.18
if (!Array.prototype.forEach) {

  Array.prototype.forEach = function(callback, thisArg) {

    var T, k;

    if (this === null) {
      throw new TypeError(' this is null or not defined');
    }

    // 1. Let O be the result of calling toObject() passing the
    // |this| value as the argument.
    var O = Object(this);

    // 2. Let lenValue be the result of calling the Get() internal
    // method of O with the argument "length".
    // 3. Let len be toUint32(lenValue).
    var len = O.length >>> 0;

    // 4. If isCallable(callback) is false, throw a TypeError exception. 
    // See: http://es5.github.com/#x9.11
    if (typeof callback !== "function") {
      throw new TypeError(callback + ' is not a function');
    }

    // 5. If thisArg was supplied, let T be thisArg; else let
    // T be undefined.
    if (arguments.length > 1) {
      T = thisArg;
    }

    // 6. Let k be 0
    k = 0;

    // 7. Repeat, while k < len
    while (k < len) {

      var kValue;

      // a. Let Pk be ToString(k).
      //    This is implicit for LHS operands of the in operator
      // b. Let kPresent be the result of calling the HasProperty
      //    internal method of O with argument Pk.
      //    This step can be combined with c
      // c. If kPresent is true, then
      if (k in O) {

        // i. Let kValue be the result of calling the Get internal
        // method of O with argument Pk.
        kValue = O[k];

        // ii. Call the Call internal method of callback with T as
        // the this value and argument list containing kValue, k, and O.
        callback.call(T, kValue, k, O);
      }
      // d. Increase k by 1.
      k++;
    }
    // 8. return undefined
  };
}

Plus this to use with html elements:

NodeList.prototype.forEach = HTMLCollection.prototype.forEach = Array.prototype.forEach;

Voila!

Arthur Ronconi
  • 2,290
  • 25
  • 25
  • Haha! I think it was my first question on SO. I totally forgot about it. It seems I was already thinking about functional programming event though didn't know the name yet :) Thanks for reminding me and +1 for the `NodeList` / `HTMLCollection` idea. – Tad Lispy Aug 26 '16 at 16:31