Section 15.4.4.20 of the ECMAScript 5.1 spec defines Array.prototype.filter(callbackfn, thisArg)
as follows:
callbackfn
should be a function that accepts three arguments and
returns a value that is coercible to the Boolean value true
or
false
. filter
calls callbackfn
once for each element in the
array, in ascending order, and constructs a new array of all the
values for which callbackfn
returns true
. callbackfn
is called
only for elements of the array which actually exist; it is not called
for missing elements of the array.
If a thisArg
parameter is provided, it will be used as the this
value for each invocation of callbackfn
. If it is not provided,
undefined
is used instead.
callbackfn
is called with three arguments: the value of the element,
the index of the element, and the object being traversed.
filter
does not directly mutate the object on which it is called but
the object may be mutated by the calls to callbackfn
.
The range of elements processed by filter is set before the first call
to callbackfn
. Elements which are appended to the array after the
call to filter begins will not be visited by callbackfn
. If existing
elements of the array are changed their value as passed to
callbackfn
will be the value at the time filter visits them;
elements that are deleted after the call to filter begins and before
being visited are not visited.
That in itself is already a lot of work; a lot of steps that the ECMAScript engine needs to perform.
Then it goes on to say the following:
When the filter method is called with one or two arguments, the
following steps are taken:
Let O
be the result of calling ToObject
passing the this
value as the
argument.
Let lenValue
be the result of calling the [[Get]]
internal
method of O
with the argument length
.
Let len
be ToUint32(lenValue)
.
If IsCallable(callbackfn) is false, throw a TypeError exception. If
thisArg was supplied, let T be thisArg; else let T be undefined. Let A
be a new array created as if by the expression new Array() where Array
is the standard built-in constructor with that name. Let k be 0. Let
to be 0. Repeat, while k < len Let Pk be ToString(k). Let kPresent be
the result of calling the [[HasProperty]] internal method of O with
argument Pk. If kPresent is true, then Let kValue be the result of
calling the [[Get]] internal method of O with argument Pk. Let
selected be the result of calling the [[Call]] internal method of
callbackfn with T as the this value and argument list containing
kValue, k, and O. If ToBoolean(selected) is true, then Call the
[[DefineOwnProperty]] internal method of A with arguments
ToString(to), Property Descriptor {[[Value]]: kValue, [[Writable]]:
true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
Increase to by 1. Increase k by 1. Return A.
The length property of
the filter method is 1.
NOTE The filter function is intentionally generic; it does not require
that its this value be an Array object. Therefore it can be
transferred to other kinds of objects for use as a method. Whether the
filter function can be applied successfully to a host object is
implementation-dependent.
Some things to note about this algorithm:
- It prevents the predicate function from mutating the data
- It optionally sets the execution context of the predicate function
- It ignores deleted values and gaps in the array
In a lot of cases, none of these things are needed. So, when writing a filter
method of your own, most of the time you wouldn’t even bother to perform these steps.
Every ES5.1-compliant JavaScript engine must conform to that algorithm, and must thus perform all those steps every time you use Array#filter
.
It should be no surprise that any custom-written method that only performs a part of those steps will be faster :)
If you write your own filter
function, chances are it’s not gonna be as complex as the above algorithm. Perhaps you won’t be converting the array to an object at all, as depending on the use case it may not be needed just to filter the array.