String.prototype.contains = function(str) {
return this.indexOf(str) !== -1;
};
This snippet does extend String.prototype.
It works well with function calls like 'foobar'.contains('foo');
But it doesn't work well if it's passed as a function, not invoking it:
var str = 'foobar';
['foo', 'bar'].every(str.contains);
TypeError: Object [object global] has no method 'indexOf'
I know you could do:
['foo', 'bar'].every(str.contains.bind(str));
or something like:
['foo', 'bar'].every(function(item) { return str.contains(item); });
But they just make me feel awkward.
In a Constructor function I created, although it doesn't work this way:
function Foo(myStr) {
this.myStr = myStr;
this.contains = function(fragment) {
return this.myStr.indexOf(fragment) !== -1;
};
}
var someFoo = new Foo('foobar');
['foo', 'bar'].every(someFoo.contains)
TypeError: Cannot call method 'indexOf' of undefined
I could do this to make it work:
function Foo(myStr) {
var self = this; // Line added
this.myStr = myStr;
this.contains = function(fragment) {
return self.myStr.indexOf(fragment) !== -1; // this changed to self
};
}
I'm wondering if there's a way to extend String.prototype so that I don't have to bind 'someString'.contains to 'someString' every time I use it as a function without invoking it.
Try this:
Object.defineProperty(String.prototype, "contains", {
get: function () {
return (function(str) {
return this.indexOf(str) !== -1;
}).bind(this);
}
});
It defines a new property with a getter, meaning every time you "get" the function, for example in ['foo', 'bar'].every(str.contains);, the function defined above would get called and the return value of that function would be returned as str.contains, in this case. The function is the same as yours, only we bind this in the getter which is the string, which is what we want.
If Object.defineProperty isn't defined in your browser (or anyone to use your code), you can use __defineGetter__ instead.
A complete solution would look like this:
addPropertyWithGetter = function(object, property) {
getter = function () {
return (function(str) {
return this.indexOf(str) !== -1;
}).bind(this);
}
if (_.isFunction(Object.defineProperty)) {
return Object.defineProperty(object, property, {
get: getter,
});
} else {
object.__defineGetter__(property, getter);
}
}
And the usage:
addPropertyWithGetter(String.prototype, "contains")
The second parameter for Array.prototype.every() is the thisArg(Value to use as this when executing callback).
So you could do with:
['foo', 'bar'].every(str.contains, str);
For fixing your code:
function Foo(myStr) {
this.contains = function(fragment) {
// myStr is accessible here
return myStr.indexOf(fragment) !== -1;
};
}
var someFoo = new Foo('foobar');
['foo', 'bar'].every(someFoo.contains);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With