6

I have added the following method to the Array prototype:

Array.prototype.foreach = function(func){
    for(var i = 0; i < this.length; i++){
        if(!func(this[i]) === false) break; //return false from func in order to break the loop
    }
    return this;
}

In the same file, after the above code, I have the following jQuery plugin:

jQuery.fn.addClassForEvents = function(){

    var that = this;

    arguments.foreach(function(event){
        that.bind(event[0], function(){
            that.addClass(event[0]);
        })
        .bind(event[1], function(){
            that.removeClass(event[0]);
        });
    });

    return this;
}

In order to use this jQuery plugin, my code would look something like:

$('div').addClassForEvents(['mouseenter', 'mouseleave']);

However, the browser throws an error on the "arguments.foreach(...." line of the jQuery plugin, stating simply that

Object # has no method 'foreach'

Yet the foreach method works in other places of my code. Why is it undefined within this jQuery plugin?

maxedison
  • 17,243
  • 14
  • 67
  • 114
  • 1
    Why do you break your loop (and foreach), when the function returns a truthy value? – knittl Jan 29 '12 at 13:48
  • That's actually a mistake (just look at my comment next to that line of code!). Thanks for pointing it out. – maxedison Jan 29 '12 at 14:48
  • That still does not make much sense within a foreach function/method.… – knittl Jan 29 '12 at 16:35
  • Why is that? Seems like providing a way to break out of a loop is pretty standard. – maxedison Jan 29 '12 at 17:08
  • Common understanding is that foreach iterates over every element of a collection. I don't know of a language, where a foreach method breaks out if the applied function returns false. Feel free to prove me wrong – knittl Jan 29 '12 at 17:53
  • Since JavaScript doesn't provide a native implementation of a foreach loop, I created one. And like all other foreach loops in every other programming language, I wanted a way to break the loop. If you've got a better way of accomplishing this, I'd love to see/adopt it. – maxedison Jan 29 '12 at 18:35
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/7146/discussion-between-knittl-and-maxedison) – knittl Jan 29 '12 at 18:41

4 Answers4

8

It doesn't work because arguments isn't an array. Its an (array-like) arguments object.

Explanation from Mozilla

You can convert it to an array using slice in modern browsers (and by actually looping in IE).

var argArray = Array.prototype.slice.call(arguments)
Hemlock
  • 6,130
  • 1
  • 27
  • 37
  • You can convert it to an array using slice in IE too – AngusC Jan 29 '12 at 19:27
  • @AngusC You could be correct. I have a generic function that does this and there are definitely some array-like objects that IE doesn't like to call slice on. – Hemlock Jan 30 '12 at 03:11
4

arguments is not an array, but an object. For example it provides properties such as arguments.callee and arguments.caller.

You can use foreach of the Array prototype by calling apply on it (cf. The JavaScript arguments object…and beyond):

Since all the methods of Array.prototype are designed to be generic they can be easily applied to the array-compatible arguments object:

jQuery.fn.addClassForEvents = function(){

    var that = this;

    [].foreach.apply(arguments, (function(event){
        that.bind(event[0], function(){
            that.addClass(event[0]);
        })
        .bind(event[1], function(){
            that.removeClass(event[0]);
        });
    });

    return this;
}
knittl
  • 246,190
  • 53
  • 318
  • 364
2

You need to turn the arguments object into an array

Try this:

jQuery.fn.addClassForEvents = function(){

    var that = this, arg = Array.prototype.slice.call(arguments);

    arg.foreach(function(event){
        that.bind(event[0], function(){
            that.addClass(event[0]);
        })
        .bind(event[1], function(){
            that.removeClass(event[0]);
        });
    });

    return this;
}
qwertymk
  • 34,200
  • 28
  • 121
  • 184
0

To convert the arguments to an array, you can use the jQuery.makeArray(arguments) too...

http://api.jquery.com/jQuery.makeArray/

lmcarreiro
  • 5,312
  • 7
  • 36
  • 63