Martijn
Martijn

Reputation: 5611

Javascript array filter: getting the context right

While trying to write some awesome JS I ran into some strange JS-behaviour. I've created a piece of code to demonstrate my problem:

http://jsfiddle.net/CeyCy/

<html>
  <head>
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script type="text/javascript">
    $(function() {
      var images = $('.images').children();
      var filtered = images.filter(function(i, image) {
        console.log(this);
      }, "abc");
    });
    </script>
  </head>
  <body>
    <div class="options">
      <div class="filters">
      </div>
      <ol class="pagination">
      </ol>
    </div>
    <div class="results">
      <ul class="images">
        <li data-category="Zakelijk">Result 1</li>
        <li data-category="Speels">Result 2</li>
        <li data-category="Blogging">Result 3</li>
        <li data-category="Zakelijk">Result 4</li>
        <li data-category="Speels">Result 5</li>
        <li data-category="Blogging">Result 6</li>
        <li data-category="Zakelijk">Result 7</li>
        <li data-category="Speels">Result 8</li>
        <li data-category="Blogging">Result 9</li>
        <li data-category="Zakelijk">Result 10</li>
        <li data-category="Speels">Result 11</li>
        <li data-category="Blogging">Result 12</li>
        <li data-category="Zakelijk">Result 13</li>
        <li data-category="Speels">Result 14</li>
        <li data-category="Blogging">Result 15</li>
      </ul>
    </div>
  </body>
</html>

Ok, now my question. When using the Array.filter method you pass in a callback function as first parameter and an optional context as second parameter. As you can see, I'm passing "abc" as context (which is ridiculous, ofcourse). I would expect my console to throw a lot of "abc", but instead it prints the jQuery elements!

Can someone shine some light on this please?

Thanks,

Martijn

Upvotes: 2

Views: 1949

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1074168

As you can see, I'm passing "abc" as context (which is ridiculous, ofcourse). I would expect my console to throw a lot of "abc", but instead it prints the jQuery elements!

You're using jQuery's filter, which doesn't have a context parameter, rather than ES5's Array#filter, because you're calling filter on a jQuery object, and jQuery objects are not arrays (although they offer a lot of array-like functionality, and several "methods" with similar names — including filter).

Three options for you:

makeArray

You can use jQuery's makeArray to turn the jQuery object into an array, e.g.:

$(function() {
  var images = $.makeArray($('.images').children());
  var filtered = images.filter(function(i, image) {
    console.log(this);
  }, "abc");
});

Updated fiddle

proxy / bind

Or alternately, you can use jQuery's proxy function (or ES5's Function#bind) to bind the iterator function you're passing to jQuery's filter so it ignores the this jQuery supplies:

$(function() {
  var images = $('.images').children();
  var filtered = images.filter($.proxy(function(i, image) {
    console.log(this);
  }, "abc"));
});

Apply Array#filter to the jQuery object

Or you can apply Array#filter directly to the jQuery object:

$(function() {
  var images = $('.images').children();
  var filtered = Array.prototype.filter.call(images, function(i, image) {
    console.log(this);
  }, "abc");
});

...since Array#filter is expressly defined to work on anything array-like, not just Arrays. That last might actually be the most efficient way to do it. Fiddle

Upvotes: 6

Related Questions