Today is “beautify ugly JS” (BUJS) day. Enjoy!
This post is part of the BUJS series, a collection of posts that focus on and discuss best practices in JavaScript by revealing the inefficiencies and code-smells of randomly selected JS code from the wild.
Today’s source: from here — sheesh, 150 upvotes!
function getParameterByName( name ) { name = name.replace(/[[]/,"\[").replace(/[]]/,"\]"); var regexS = "[\?&]"+name+"=([^&#]*)"; var regex = new RegExp( regexS ); var results = regex.exec( window.location.href ); if( results == null ) return ""; else return decodeURIComponent(results[1].replace(/+/g, " ")); } |
What’s wrong with it?
Well, nothing, but at the same time, everything!
- WTF is the first line about? I guess the intention is to handle when
name
is something likemeh[]
. The[]
isn’t standardised, AFAIK. Also, WTF is the point if it’s only going to return the first value ofmeh[]
. Either give it full support ("?meh[]=1&meh[]=2" -> [1,2]
) or give it NO support. - Why exactly is
regexS
stored whatsover… it’s only used once elsewhere. Readability won’t suffer at all if we were to simply place the assigned expression where it’s eventually needed. - We don’t need to escape
?
when it’s in a regular-expression character-class. It’s in a character class FFS! Let’s rid ourselves of that\
malarky! - Cool kids know that the
new
operator isn’t always required. Readability matters. I will assume the person reading the code has read the spec! RegExp.prototype.exec
can only return a match array (ofArray
type) or null.null
is the only potential falsey return value — may as well be cool and rid ourselves of== null
.- Two return statements… where’s the delicious ternary operator? (or “conditional operator”) — cool people use this.
- Use
location.search
instead oflocation.href
so we can do-away with handling#...
things (in the original regex:=([^&#]*)
).
// Revised, cooler. function getParameterByName(name) { var match = RegExp('[?&]' + name + '=([^&]*)') .exec(window.location.search); return match ? decodeURIComponent(match[1].replace(/+/g, ' ')) : null; } |
EDIT: I posted my solution on SO, if you feel so obliged to upvote! 😉
EDIT2: As Andy E. noted in his comment (@ StackOverflow) I could shorten it further by using return match && decodeURIComponent(match[1].replace(/+/g, ' '));
instead of the ternary operator. This totally slipped my mind. The &&
operator will return the left operand if the right operand is falsey and since .exec()
returns null
anyway this works out nicely! I’ll leave the original improved version intact here, see the updated one here.
Potential improvement: protect against ill-use by escaping Regex characters in name
… also potentially String(name)
to make it almost bulletproof.
Join me next time. This series’ll rock!
Thanks for reading! Please share your thoughts with me on Twitter. Have a great day!
I’d love to see the “escape Regex characters in name” part implemented…
@Alvaro, this’d work:
So, with potential improvements, let’s go with:
I’m still not a big fan of this approach. It reeks of inefficiency, not in the way seasoned JavaScript developers would use it — we know to store and re-use the result. A user unfamiliar with JavaScript, however, may go about executing the function with the same argument over and over in loops without storing and re-using the result. You see that sort of thing all the time with jQuery —
$(this).attr("id");
occurs several times in the same block.Not sure if you took a look at my answer on the same question — it only runs once and parses all the values into a single object, as well as supporting parameter existence without values and is easily improved to support array-style params (example).
All that being said, it’s a nice improvement to the original code and is a lot less of an eyesore. +1, as we say 🙂
@Andy, I agree 100%. To be honest, when I did this I wasn’t thinking so much about application but simply about code clarity/readability and micro-efficiency.
@Everyone, if you care, and want to use this for your app, please use Andy’s answer or at least add some caching in, e.g.
@James,
That’s also a very nice workaround. Looking forward to BUJS #2 – maybe I’ll join in if I come across some horrendously ugly code 🙂
@James – Nice bit, thank you. I’ve always wondered why JavaScript lacks a native RexExp method to quote a literal.
I’m usally generating an JS-object out of the parameters. That makes many things a whole lot easier – and prevents me from using RegExps. Besides, if you plan to look up multiple parameters, my solution will perform better. Just to illustrate the main idea (the code hasn’t been tested):
Love the article dude – you could also have picked up on the three var ; declarations right after each other that I guess could have been combined!
I wouldn’t have spotted those crazy optimisations though. As soon as I see regexes my eyelids feel really heavy.
@Felix, Andy E. mentioned that idea and FWIW, if we were talking about potential applications of the function I would agree, but the purpose of BUJS is simply to take ugly code and make it prettier and more efficient — even if it is fighting the wrong battle.
@Alvaro, I agree–it should be supported natively.
@Joss, thanks! 🙂
Iam getting these errors in w3c validation in the “&” plz can anybody help
Line 166, Column 27: xmlParseEntityRef: no name
var match = RegExp(‘[?&]’ + name + ‘=([^&]*)’)
@Bush Tag: It’s pretty normal that JavaScript code won’t pass HTML validation, isn’t it?
@James,
I wanted to pass a link through the query string,… but think instead that it would make more sense to parse through the query on the receiving page to create a link.
What I’m trying to do is to send the name of an image.jpg through the query string, and then provide a link to a hi-res version of the image on the receiving page.
@Felix Your code will have issue if the value of a key is a base64 encoded string.