Reputation: 1631
I'm learning javascript right now, seems like beautiful functional language to me, it is wonderful move from PHP, I should have done this earlier. Although, I cannot figure this one out:
var v1 = (/[abc]/).test;
v1('a');
says test method called on incompatible undefined
, I'm trying to store the test method of that regex into variable and invoke it later.
but it works with my own functions:
function foo(){
return 'I\'m foo';
}
var f = foo;
f(); // returns I'm foo
It should work on methods too, since functions are just methods of parent object anyway, right?
Ultimately, the reason I'm trying this is to be able to write something like this:
var a = ['a', 'b', 'c'];
a.every( (/[abc]/).test );
to check each array member against that regex.
Why doesn't this work? Is it limitation in passing built-in functions around? Or am I just doing something wrong?
PS: If you grind your teeth now and muffling something about bad practices, screw good practices, I'm just playing. But I'd like to hear about them too.
Upvotes: 2
Views: 225
Reputation: 816760
it works with my own functions
You are not using this
inside the function. Consider this example:
var obj = {
foo: 42,
bar: function() {
alert(this.foo);
}
};
var f = obj.bar;
f(); // will alert `undefined`, not `42`
It should work on methods too, since functions are just methods of parent object anyway, right?
"Method" is just a colloquial term for a function assigned to a property on object. And functions are standalone values. There is no connection to the object a function is assigned to. How would this even be possible, since a function could be assigned to multiple objects?
Why doesn't this work?
What this
refers to inside a function is determined at run time. So if you assign the function to a variable and call it later
var v1 = (/[abc]/).test;
v1('a');
this
inside the function will refer to window
, not to the regular expression object.
What you can do is use .bind
[MDN] to explicitly bind this
to a specific value:
var a = ['a', 'b', 'c'];
var pattern = /[abc]/;
a.every(pattern.test.bind(pattern));
Note though that since .bind
returns a function, the only advantage over using a function expression is that it is a tad shorter to write.
Is it limitation in passing built-in functions around?
No, the problem exists for every method/function because that's how functions work. The nice thing about built-in functions though is that they often explicitly tell you when this
is referring to the wrong type of object (by throwing an error).
Upvotes: 3
Reputation: 22979
The test
function expects this
to be a regular expression. The expression /[abc]/.test
gives an unbound function (it does not remember that it belongs to /[abc]/
). When you invoke it like you do, this
will be undefined
and the function will fail.
You can use bind
to make the function remember the object it belongs to:
var v1 = /[abc]/.test.bind(/[abc]/);
or
var v1 = RegExp.prototype.test.bind(/[abc]/);
Upvotes: 1
Reputation: 64657
I don't know if this is an acceptable solution, but you can do:
v1 = function(exp) { return (/[abc]/).test(exp); }
v1('a');
Upvotes: 0
Reputation: 8616
Your reference to the method has lost its knowledge of what it was a method of. This isn't so much good practice as just the way JS works.
You can do:
var v1 = /[abc]/;
v1.test('a');
If you must encapsulate the test method, then you could do:
var v1 = function(str){
return /[abc]/.test(str);
};
v1('a');
Upvotes: 0
Reputation: 707686
If you store just a method, it does not carry with it a reference to your object - it just stores a reference to the .test
method, but no particular object. Remember, a method is "just" a property on an object and storing a reference to a method doesn't bind it to that object, it just stores a reference to the method.
To invoke that method on a particular object, you have to call it with that object.
You can make your own function that calls the method on the desired object like this:
var v1 = function(x) {
return /[abc]/.test(x);
}
Then, when you do this:
v1('a');
It will execute the equivalent of this in your function:
/[abc]/.test('a');
But, it isn't entirely clear why you're doing that as you could also just define the regex and call .test() on it several times:
var myRegex = /[abc]/;
console.log(myRegex.test('a'));
console.log(myRegex.test('b'));
console.log(myRegex.test('z'));
Upvotes: 3