5

This doesn't work:

var s = '^foo';
console.log(['boot', 'foot'].some(s.match));

Uncaught TypeError: String.prototype.match called on null or undefined

But this does:

var s = '^foo';
console.log(['boot', 'foot'].some(function(i) { return i.match(s) }));

Why is this? I imagine somehow the String.prototype.match function is too "primitive" or something, but why exactly? Since I'm not using ES2015, the second version seems quite verbose. Is there an alternative?

EDIT

When I wrote the above, I actually got it backwards compared to my actual need, which was matching one string against a number of regexes. But thanks to the great answers and comments below, I get it: [/^foo/, /^boo/].some(''.match, 'boot').

Steve Bennett
  • 114,604
  • 39
  • 168
  • 219
  • Don't you mean `i.match(s)`? Not that it matters really. – James Nov 10 '15 at 05:35
  • `['boot', 'foot'].some(/./.test, /^foo/)` works as expected since test() lives on the regexp, which can be passed in to some(), whereas you can't call the per-item string-based match() method without a wrapper on the `[].some()` callback. so, if you were using `"".match()` to `filter/some/every`, simply use `/./.test()` instead and pass the regexp pattern as the 2nd argument to the array method. – dandavis Nov 10 '15 at 05:47
  • @dandavis That's pretty interesting, why not make that an answer? – Steve Bennett Nov 10 '15 at 11:34

2 Answers2

6

Note: The value of this is determined by how the function is called! (exception: bound and arrow functions)

If you pass s.match to .some, then the function will be called with this set to the global object (e.g. window) not the string it "belongs" to.

I.e. it would be equivalent to this:

String.prototype.match.call(window, 'foo')

This cannot work because this has to refer to a string object.

You could solve this by binding the function to a specific this value:

['boot', 'foot'].some(s.match.bind(s));

Learn more about this:

Community
  • 1
  • 1
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • Same result but you could also refer to the function on the String prototype: `['boot', 'foot'].some(String.prototype.match.bind(s));` Seems more "pure" to me but who knows. – ach Nov 10 '15 at 05:37
4

A function value in Javascript does not bring its object along with it. The value of s.match is a plain function value, with no knowledge that you happened to find it attached to s. In fact, no matter what String you access it through, it's always the same function value:

"foo".match === "bar".match 
//= true

When you call a function through an object, Javascript sets this to that object for the duration of the function call. But as soon as anything comes between retrieving the function value and calling it, any object association is lost.

You can create a function that does remember a specific this value using bind, as in @Felix King's answer. someFunction.bind(someObject) has approximately the same meaning as function(arg1, arg2,...) { return someObject.someFunction(arg1, arg2,...); }, but it automatically handles the number of parameters properly.

Mark Reed
  • 91,912
  • 16
  • 138
  • 175