1

I recently wrote a couple of extensions to the JS array prototype

Array.prototype.diff = function (a) {
    return this.filter(function (i) {
        return !(a.indexOf(i) > -1);
    });
};

Array.prototype.udiff = function (a, b) {
    var rslt = this.concat(b);
    return rslt.filter(function (i) {
        return !(a.indexOf(i) > -1);
    });
};

Nothing terribly exciting there. But then I ran into something quite unusual. Here is an example

var arr = [];
for (prop in arr) {
    arr[prop].attrib = arr[prop].attrib.replaceAll('_', ' ', true);
}

Quite an innocent looking piece of code but it came back to me with an error along the lines of "undefined does not have method replaceAll - where replaceAll is my own String.prototype extension.

The solution is simple - before manipulating arr[prop] just issue

if ('string' == typeof(prop)) continue;

The reason being that prop can also be diff or udiff. So, problem solved but this behavior did take me off my guard and having to do the additional typeof test does sound clumsy. Perhaps someone here has deeper insights into what happens with prototype extensions?

I should mention that all of these issues occured in Chrome on Windows.

Hunter McMillen
  • 59,865
  • 24
  • 119
  • 170
DroidOS
  • 8,530
  • 16
  • 99
  • 171
  • 1
    possible duplicate of [Why is using "for...in" with array iteration such a bad idea?](http://stackoverflow.com/questions/500504/why-is-using-for-in-with-array-iteration-such-a-bad-idea) or [How to define method in javascript on Array.prototype and Object.prototype so that it doesn't appear in for in loop](http://stackoverflow.com/questions/13296340/how-to-define-method-in-javascript-on-array-prototype-and-object-prototype-so-th) – Bergi Sep 06 '13 at 12:48
  • 1
    defineProperty allows to make properties not enumerable (i.e this is not the right way to add a method to an object type). – Virus721 Sep 06 '13 at 12:50

2 Answers2

2

See this answer for a detailed explanation


problem solved but this behavior did take me off my guard and having to do the additional typeof test does sound clumsy.

The proper method is not to use for in loops on arrays but iterate them until their length:

for (var i=0; i<arr.length; i++) {
    arr[i].attrib = arr[i].attrib.replaceAll('_', ' ', true);
}
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
0

Well the for each loop in javascript iterates over all the symbols in an object (including those in the prototype). That's why your getting the result that you did. That's also why you should always use the function hasOwnProperty() in combination with the for each loop.

var arr = [];
for (prop in arr) {
    if(arr.hasOwnProperty(prop)) {
        arr[prop].attrib = arr[prop].attrib.replaceAll('_', ' ', true);
    }
}
ComFreek
  • 29,044
  • 18
  • 104
  • 156
A. Tapper
  • 1,261
  • 8
  • 17
  • 1
    No. You can reasonably expect objects that are worth enumerating to have no un-intended inherited enumerable properties, so no need for `hasOwnProperty`. For arrays, you should just use proper `for(var i=0; i – Bergi Sep 06 '13 at 12:59
  • Good point, @Bergi. Make that an answer and I will accept it. – DroidOS Sep 06 '13 at 13:03
  • -1, `for (var p in a)` is the *wrong* way to iterate the values in an array, no matter how much you "fortify" it. – just somebody Sep 06 '13 at 13:37