2

Recently when working on a project in javascript I tried something similar to this snippet. I was surprised to find out it doesn't work and instead throws an error.

const test = [1, 2, 3, 4];

const something = [1, 2, 3, 4, ,5, 6, 7, 8].filter(test.includes);

console.log(something);

TypeError: Cannot convert undefined or null to object
    at includes (<anonymous>)
    at Array.filter (<anonymous>)
    at evalmachine.<anonymous>:3:45
    at Script.runInContext (vm.js:74:29)
    at Object.runInContext (vm.js:182:6)
    at evaluate (/run_dir/repl.js:133:14)
    at ReadStream.<anonymous> (/run_dir/repl.js:116:5)
    at ReadStream.emit (events.js:180:13)
    at addChunk (_stream_readable.js:274:12)
    at readableAddChunk (_stream_readable.js:261:11)

Is this a javascript bug or am I misunderstanding something here. The following snippets work fine:

const test = [1, 2, 3, 4];

const something = [1, 2, 3, 4, ,5, 6, 7, 8].filter(item => test.includes(item));

console.log(something);

And:

const test = [1, 2, 3, 4];

const someFunc = theThing => test.includes(theThing);
const something = [1, 2, 3, 4, ,5, 6, 7, 8].filter(someFunc);

console.log(something);

Working towards a more functional style of programming and point free when I see the patterns easily enough, this seems like an inconsistency.

Edit: This is not a duplicate. I don't need clarification on This, just on how it was handled in the context of the includes function specifically.

  • Passing `test.includes` breaks the relationship between the `includes` method and the object `test`. Wrapping it in a function like you did or using `.bind()` fixes the problem. – Pointy Feb 11 '19 at 14:56

2 Answers2

6

You could use thisArg of Array#filter and the prototype of the function of the object.

const test = [1, 2, 3, 4];

const something = [1, 2, 3, 4, ,5, 6, 7, 8].filter(Array.prototype.includes, test);

console.log(something);

But Array#includes has a second parameter fromIndex, which is handed over by the filter function and that is the index, which may be unwanted.

In this case, you may better use the direct style.

const test = [1, 2, 3, 4];

const something = [1, 2, 3, 4, ,5, 6, 7, 8].filter(v => test.includes(v));

console.log(something);
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • The latter is what I ended up doing. I was just surprised it didn't work the way I was expecting it to. Your explanation makes sense though, thank you for th e clarification. – Jason Matthews Feb 12 '19 at 15:08
1

test.includes evaluates to a reference pointing to a function, and through that its context (aka this) gets lost.

 const fn = test.includes;
 fn(1); // doesnt work
 fn.call(test, 1); // does work as you explicitly set `this`.

You can easily fix it by using the second parameter of filter,

 .filter(test.includes, test)
Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151