Back to Basics

Posted in: javascript
Most of us JavaScript developers can't work without using a JavaScript Library/Framework today. JavaScript is a very nice language, but it requires for you to write a lot of code before you can implement some interesting animation, drag and drop feature, or Ajax request/polling. This is in part due to the fact that every line of code we write must be browser compatible, and that adds lots of "ifs" to our code. So I guess it's understandable to think that choosing a framework that can abstract these kind of things for us is good. By using a framework we can concentrate on other kind of problems, more like high-level-usability-pattern problems. Some frameworks stick with JavaScript as their main language (like MooTools or JQuery). Other frameworks simply let you type another kind of language that then gets compiled into JavaScript. I guess that having a language with built-in classical inheritance syntax and a nice IDE to support it are good reasons to develop these kind of libraries that translate some code into another. Frameworks are undeniably good, but most of the things I learn about the JavaScript programming language were learn while hacking some pure JavaScript code. When hacking pure JavaScript code you find yourself hacking common language idioms that are usually abstracted by frameworks. And it's nice to understand how these things work. When you get how a specific JavaScript pattern/idiom works you get to understand lots of things about the language itself. Most of the people don't do this today. You can see posts about call and apply, closures and private members patterns very often in Reddit and Ajaxian, and each time that post appears lots of other people upmod it. So that means that most of the people today probably use the bind Function method without really knowing what it does. The worst part is that JavaScript is a very beginner-friendly language, a good start for people not having a computer sicence related background, but at the same time there are lots of things about the language itself that are advanced features (object mutability, booleans as defaults, functions as first class citizen, prototypal inheritance) and most of the users are never aware of these features, most of the time due to the abstraction of the frameworks they're using.

An Example

This is a very simple example (and interesting interview question also). If you're dealing with the dom when hacking JavaScript then you might often use the hasClass and removeClass methods from JQuery, MooTools, or whatever. So, how would you write them?
function hasClass(domElement, className) {
 //code here...
}

function removeClass(domElement, className) {
//code here...
}
Please, if you're reading this, take five minutes of your time and write these functions out before reading the answers. Believe me, it's worth the effort. I mean, how much time can it take?

The Answers

The answer is quite simple, also, you can go and check the answer in the MooTools or JQuery source code. So for the hasClass function the answer is this:
function hasClass(domElement, className) {
  return (' ' + domElement.className + ' ')
        .indexOf(' ' + className + ' ') >= 0;
}
Basically we add one space at the beginning and end of each string, and see if the className property of the domElement contains the className string provided. Why adding those spaces? Well, if we don't add spaces then we could have problems with names containing the given className. Also, the code is concise and most of the work is done by the built-in indexOf method, which is good for performance. Did you get that answer well? Good! How about your removeClass function? The answer is this:
function removeClass(domElement, className) {
 domElement.className = domElement.className
      .replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1');
}
I think this RegExp is better explained with an example. Lets say that:
var className = 'myclassname';
domElement.className = 'myclassname yeah';

removeClass(domElement, className);
Then the RegExp will be
'(^|\\s)myclassname(?:\\s|$)'
Lets make it simpler first:
'(^|\\s)myclassname(\\s|$)'
Ok, so that's simple enough. So this regexp means that either we're at the beginning of the string (^) and searching for our className having a space at the end ('^myclassname\\s') or simply ending with that className ('^myclassname$') or we're looking for our className string having a space at the beginning and end ('\\smyclassname\\s') or we're at the end of the string looking for our className having (or not) a space at the beginning of the string ('\\smyclassname$' or '^myclassname$'). This regexp is equivalent to (if we had startsWith and endsWith methods):
domElement.className == className //means '^className$'
|| domElement.className
          .startsWith(className + ' ') //means '^className\\s'
|| domElement.className
          .endsWith(' ' + className) //means '\\sclassName$'
Ok, so once the RegExp matches it's replaced by '$1', which means the first capturing group, in this case the (^|\\s) group. So back to our example, the domElement.className was: 'myclassname yeah' so the RegExp that matches is ^myclassname\\s and the capturing group is ^, so the returned string is 'yeah', just as espected. What (?: ...) does is not to make a capturing group from the parenthesis match. I think this is good for performance f you're not using that group in the string replacement: Non-Capturing Parentheses are of the form `(?:<regex>)' and facilitate grouping only and do not incur the overhead of normal capturing parentheses. They should not be counted when determining numbers for capturing parentheses which are used with backreferences and substitutions. Because of the limit on the number of capturing parentheses allowed in a regex, it is advisable to use non-capturing parentheses when possible.

Conclusion

These methods could have probably been written differently if disk space and performance weren't such an important issue in JavaScript, but with those constraints even the most trivial methods like hasClass and removeClass can be interesting to read. So this is my recommendation: try to understand how things you normally use are implemented. There are lots of interesting JavaScript libraries that make excellent code that's performant and save disk space (:P) so check them out, you might learn about lots of things, even the most simple functions can have interesting concepts.
Comments
blog comments powered by Disqus