Array filter returns strange results

2019-05-21 16:24发布

问题:

Related to this question, i wanted to try out this

var arr = [0,1,2,true,4,{"abc":123},6,7,{"def":456},9,[10]];
arr.filter(Object.hasOwnProperty,"abc");//outputs [0, 1, 2]
arr.filter(Object.hasOwnProperty,"2222222") //[0, 1, 2, 4, 6]

Does anyone knows why filter return these values? Spec of filter and MDN doc also doesn't clearly tell how second argument of filter is used.

回答1:

The second argument to the Array.prototype.filter is the value that will be set as this to the function that is passed as a first argument.

So your code ends up to be something like:

arr.filter(function(v, i, a) {
    return Object.hasOwnProperty.call("222", v, i, a);
});

So it basically checks if the "222" string has the properties you enumerate in the array.

From that it becomes clear why properties 0, 1 and 2 are found - since those are the indexes of the characters in the "222" string, and, say, 9 or {"abc":123} are not - since "222" string does not have such a properties.

It is the same story with the longer string, which also includes properties 4 and 6 just because it's longer.

Some examples:

Object.hasOwnProperty.call("222", 1); // true, because `"222"[1]` is there
Object.hasOwnProperty.call("222", 'foo'); // false, because `"222"['foo']` is not there


回答2:

It's crystal clear from the spec

Array.prototype.filter ( callbackfn [ , thisArg ] ),

If athisArg parameter is provided, it will be used as the this value for each invocation of callbackfn.

So:

var arr = [0,1,2,true,4,{"abc":123},6,7,{"def":456},9,[10]];
arr.filter(Object.hasOwnProperty,"2222222");

translates to these calls, in sequence

"2222222".hasOwnProperty(0);             // true     -> 0
"2222222".hasOwnProperty(1);             // true     -> 1
"2222222".hasOwnProperty(2);             // true     -> 2
"2222222".hasOwnProperty(true);          // false    -> 
"2222222".hasOwnProperty(4);             // true     -> 4
"2222222".hasOwnProperty({"abc":123});   // false    -> 
"2222222".hasOwnProperty(6);             // true     -> 6
"2222222".hasOwnProperty(7);             // false    -> 
"2222222".hasOwnProperty({"def":456});   // false    -> 
"2222222".hasOwnProperty(9);             // false    -> 
"2222222".hasOwnProperty([10]);          // false    -> 
                                         // filter() => [0,1,2,4,6]

The lines where it says true are because strings can be indexed into like arrays, so a string with two characters has the indexes 0 and 1 as own properties.