Quick Tip: How to Extend Built-in Objects in JavaScript
Constructor functions, like Array, offer a wide range of methods and properties that you can make use of. But have ever wished that one of these objects offered some method that isn’t built-in? Is there a way to do so yourself? Absolutely! Let’s see how.
Reversing a String
This little snippet takes advantage of the Array object’s “reverse” method, and applies its functionality to a given string. In effect, something like “hello” will be turned into “olleh,” and can be accessed by using “myString.reverse()”.
String.prototype.reverse = function() {
return Array.prototype.reverse.apply(this.split('')).join('');
};
var myString = 'hello';
console.log(myString.reverse());
Bonus Question
Now that we have a basic of understanding of augmenting objects, can you figure out a way to write a custom “contains” method for the String object? For example, the jQuery library allows us to write things like:
$("div:contains('John')").css('fontWeight', 'bold');
The snippet above will search through all of the divs on the page, and then filter that wrapped set down to only those that contain the string “John.” How could we extend the String object, with raw JavaScript, to allow for this? Leave your solution in the comments, and we’ll discuss!
- Follow us on Twitter, or subscribe to the Nettuts+ RSS Feed for the best web development tutorials on the web.










Perhaps the better question is why you would want to extend the string object to make element selections…
That seems far out of the bounds of rational OOP.
Traditionally you would make a custom type that manages a particular type of selection and distill that to a prototype method.
Grooming aspiring JS developers to extend primitive types for DOM scraping doesn’t seem well-advised in my book.
So if you’re stumped on the Bonus question, here’s what I came up with.
<body> <p>Hello World</p> <script type="text/javascript"> var para = document.getElementsByTagName("p")[0].innerHTML; String.prototype.contains = function(word) { return !!( new RegExp(word, "ig").test(this) ); }; if ( para.contains("hello") ) { alert("Matched!"); } </script> </body>Got something better?
What about?
String.prototype.contains = function(word){
return !!(this.indexOf(word) + 1);
}
String.prototype.contains = function(word){
return !(this.indexOf(word)=== -1);
}
String.prototype.contains = function(word){
var rx = new RegExp((^|\\s) + word + (\\s|$));
return rx.test(this);
}
I am still learning JS here (btw, loving the JS from null series, keep it up!) but I’m curious as to why you need to call prototype again on the Array within the string prototype call?
It seems to me like the prototype in array isn’t necessary because it works as array.reverse… but is there something that’s doing that is incorrect and I should therefore avoid?
String.prototype.contains=function(s){return this.indexOf(s) > -1;};
Also, your reverse example could be simplified to:
this.split(”).reverse().join(”)
It might be worth mentioning, just for readers’ sake (just encase they all run off and start extending everything!), that you should never extend “Object.prototype”… It’s generally considered a bad practice as doing so will, since everything is an object, extend EVERYTHING… which can have some fishy results.
Also, the addition to native methods is a controversial topic in itself. The Prototype and MooTools libraries use it heavily and they have come under heavy fire for it in the past. I think it’s mostly down to preference, although obviously you have to consider future compatibility (as you mentioned) etc..
Also, there can be some cross-browser issues when dealing with element prototypes, specifically in IE.
Was just going to post the comment.
String.prototype.contains = function(needle) {
if(haystack.indexOf(needle) > -1){
return true;
}else{
return false;
}
}
Congrats on the solution
Your code smells
(a boolean is a boolean is a …)
String.prototype.contains = function(needle) {
return haystack.indexOf(needle) > -1
}
Nice one James.
Absolutely, in reference to extending the native methods argument. I spoke about that a bit at the end of the screencast…but it probably requires more discussion.
Also – should be noted that, though I’m 100% that the indexOf method will be faster, it doesn’t allow for case-insensitive matching…at least not to my knowledge.
But yeah – very smart.
To those who are newer to JS, what James means by “extending object.prototype” is that, because prototype is an object itself, you could then technically extend the prototype’s prototype! But things quickly become too hard to maintain, and as James mentioned, it’s considered a bad practice.
toLowerCase() on the needle and haystack before check = case-insensitive
So what’s your completed code snippet?
sorry
String.prototype.contains = function(needle) {
return this.toLowerCase().indexOf(needle.toLowerCase()) > -1;
};
Beat me to it. That’s exactly what came to mind when I read the bonus question.
100% agree with James. For example, people should check the article written by Nicholas C. Zakas
named a href=http://www.nczonline.net/blog/2010/03/02/maintainable-javascript-dont-modify-objects-you-down-own/ rel=nofollowMaintainable JavaScript: Don’t modify objects you don’t own/a, to see why they should not extend build in objects.
If you are going to extends you have to make sure you are not overwriting existing methods. I good way to do it is:
if(!String.prototype.reverse){
String.prototype.reverse = function() {
return Array.prototype.reverse.apply(this.split()).join();
};
}
or
if(typeof String.prototype.reverse !== function){
code here
}
I would recommend their use only in personal project like a portfolio websites where you are free to do all you want and nobody else can overwrite your code.
String.prototype.contains = function (what) {
return !(this.indexOf(what) == -1);
}
Can someone explain how this works.
function (a, b) {
return a.compareDocumentPosition(b) & 16;
}
“compareDocumentPosition”, as the name would suggest, allows you to compare the document position of two DOM nodes. It returns a bitmask, i.e. the numbers 1, 2, 4, 8 or 16 dependent on the result.
“&” is the bitwise AND operator, and will return a 1 in every bit position that is 1 in both its operands. It’s kinda complex, and I generally stay away from bitwise ops in JavaScript because (as Crockford said), they’re slow! (and hard to understand)
Your function will end up returning the number 16 if “b” is contained by “a” (i.e. “a” is an ancestor of “b”). I’m presuming you got that from jQuery’s source. It’s actually a bug, since it should return a boolean. I believe it’s been fixed in the latest nightlies.
Read more: https://developer.mozilla.org/en/DOM/Node.compareDocumentPosition
Thanks.
Yeah, got it from jQuery source (using the source viewer you made).
http://jsfiddle.net/NmDTG/
Returns True or false whether it finds the string or not, but James’ version is a little simpler.
Cheers!
Yeah – I’m thinking that would be the next best solution, if you need it to be case-insensitive. That way, you don’t have to muddy up the script with the two “toLowerCase” calls.
I need to look up how jQuery specifically handles :contains.
Have you guys seen James’ handy dandy source viewer?
http://james.padolsey.com/jquery/#v=1.4&fn=contains
If you’re looking for the “:contains” selector filter (as opposed to jQuery.contains) then you’ll want: http://james.padolsey.com/jquery/jQuery.expr.filter.PSEUDO
Oh cool. Was trying to figure out how to search for that. Thx.
I’ve been wondering this lately but what !! actually does? Jeffrey used it in his example.
Not really necessary in my example. It forces a Boolean (true/false) conversion.
Here’s what I came up with:
String.prototype.contains = function (s, c) {
return new RegExp(s, (c) ? ‘i’ : ”).test(this);
}
Obviously, the second parameter is for case sensitivity.
It seems pointless to extend the String class for a ‘contains’ method, given that it already has the ability to do this via indexOf(). But, I suppose you could wrap it in a prototype if you find it unbearably ugly or something…
String.prototype.contains = function ( substr ) { return this.indexOf ( substr ) !== -1; }
I generally agree – even if IndexOf isn’t case insensitive. This was mostly to demonstrate how you COULD do it if you wanted. Plu, it makes for good discussion.
Code reuse, clarity, and easier to type. If I were going to perform this type of operation several times through an app, I’d wrap the functionality as a contains() method or something similar.
The jQuery contains method does not return a simple true or false value for each of the nodes it finds containing the specified string (whereas simply running an indexOf type method would), it returns an object containing a reference to all the DOM nodes that contain the string value and therefore using the each method could all be manipulated by other methods or properties.
Right. I wasn’t speaking of 100% mimicking that functionality – just a simple illustration of how to create that type of effect.
I don’t get why you need to use Array.prototype within the function itself…read the above comments but none the wiser? :S
/*
Replaces first letter of the word with Uppercase and rest all with Lowercase.
usage: “heLLo woRLD”.capitalize();
*/
String.prototype.capitalize = function() {
return this.replace(/\w+/g, function(a) {
return a.charAt(0).toUpperCase() + a.substr(1).toLowerCase();
});
};
/*
Replaces multiple white spaces and tabs with single white space.
usage: “Hello World. “.squeeze();
*/
String.prototype.squeeze = function() {
return this.replace(/(\t|\s+)/gm, ” “);
};
It’s important to note that you do not have to reference the Array prototype as was done above. Instead, you can just do:
String.prototype.reverse = function() {
this.split(”)
.reverse()
.join(”);
};
Although both cases are correct.
And there is one case in which you reference Array.prototype that is extremely useful. This is when you want to deal with a function’s arguments as a native array, which it is not by default. If you tried to work with the array object in a forEach or another Array method it would likely cause problems.
So, to turn arguments into an array, you just need to do this:
var func = function() {
var args = Array
.prototype
.slice
.call(arguments);
};
I hope the spacing in my comment sticks :/
Hi Jeff, I have a short question, how do you do the ‘beginhtml’ thing every time in these screeencasts? Is it something like a macro?
If you’re on a Mac, check out TextExpander. If PC, see Texter.
If Windows7 don’t use Texter. Screws up the drag and drop functionality of Windows.
String.prototype.contains = function(s, c) {
return (c ? ((this.indexOf(s) >= 0) ? true : false) : (this.toLowerCase().indexOf(s.toLowerCase()) >= 0) ? true : false);
}
Obfuscated, I know, but this has support for using indexOf (which I believe is faster than regexp), and support case and non-case sensitive checks.
Oh, and
/*
Usage, myString.contains(String, Boolean). The first parameter is an argument to be checked against, while the boolean indicates whether to use case sensitive search.
*/
Great screen cast.
But I want to go back to the last remark Jeff has made. He tells us to think about the future, and what it tomorrow, the new javascript engine running in your browser will give this method on the string.
The inline implementation of the browser will always be preferable to any javascript implementation we can do, so what you really want to do it check if this method exists already, and only if not, THEN add it.
You do this like so:
if(!String.prototype.reverse){
String.prototype.reverse = function() {
return Array.prototype.reverse.apply(this.split(”)).join(”);
};
}
This way, if the browser has an implementation then you will use it, and if not, your script will still function since you implement it yourself
I would be curious to see the load time of an all JavaScript based website.
good article
but iam so sorry , iam not good in js
thanks alot