Like most web developers, I spend my days giving instructions to
computers. These instructions generally involve some input (a request
for a web page), some logic (get the right content from a database) and
some output (send the content to the requesting browser). This process
of telling a computer how to perform a task, such as generating a web
page, is what we commonly call “programming,” but it’s only a subset of programming: imperative programming.
There’s another type of programming, declarative programming, that
most web developers also use every day but don’t often recognize as
programming. With declarative programming, we tell a computer what, not how.
We describe the result we want, and the details of how to accomplish it
are left to the language interpreter. This subtle shift in approach to
programming has broad effects on how we build software, especially how
we build the future web.
So, let’s take a moment to investigate declarative programming and the web we can build with it.
Hidden In Plain Sight
Declarative languages tend to fade into the background of
programming, in part because they’re closer to how we naturally interact
with people. If you’re talking with a friend and you want a sandwich,
you don’t typically give your friend step-by-step instructions on how to
make the sandwich. If you did, it would feel like programming your
friend. Instead, you’re far more likely to talk about the result you
want, such as “Please make me a sandwich” (or, perhaps, “Sudo make me a sandwich”).
If your friend is willing and able to follow this instruction, then
they would translate the phrase “Make me a sandwich” into a series of
steps, such as finding a loaf of bread, removing two slices, applying
toppings, etc.
This type of result-focused instruction is how declarative programming works,
by leaving the logic of how to implement requests to the system that is
interpreting the language (for example, your friend). When we want an
image in an HTML document, for example, we simply include an <img>
tag, and then the system that interprets the HTML (typically, a
browser) would handle all of the steps needed to display that image,
such as fetching it from a server, determining where exactly to render
it, decoding the binary data, scaling the image and rendering it to the
screen. We don’t have to explain any of this, so we often forget that
it’s all happening and that someone programmed both how it happens and
how that complex process is derived from a simple <img>.
Another factor that makes declarative programming hard to see as
programming on the web is that it “just works.” A lot of work went into
making languages like HTML, CSS and SQL capable of providing enough
clarity on what needs to be accomplished that the steps required to
achieve a result can be determined without detailed instruction. But
most web developers began using these declarative languages long after
the hard work of building them was complete, so we just see them as normal and ordinary and just a natural part of the way the web works.
When web developers do get involved in declarative
programming before the interesting work is done, it’s typically while
developing an API for a website. Most APIs are implemented via declarative programming.
Rather than provide a way to give a website step-by-step instructions,
APIs usually have a simple language that can be used to express the
desired result. When we want to get some tweets from Twitter’s API, for
example, we give a description of the tweets we want, such as
“everything from @A_single_bear.” If the API is imperative, we would
instead describe the specific steps we want Twitter to implement on our
behalf, explaining how to load, format and return the tweets.
Thankfully, the API hides all of that logic behind a simple declarative language, so we only need to describe what we want, not how to get it.
Two Paths Forward
Once we realize how widespread declarative programming languages are
on the web, it’s hard to imagine the web without them. Hard, but not
impossible. As JavaScript has grown to be ubiquitous, the tools we would
need for an imperative-only web are easy to find. We could swap out
HTML and CSS for rendering directly in JavaScript. We could swap out SQL for a JavaScript-native database (or two). And we could swap out calls to declarative web APIs with imperative calls to JavaScript functions, even across the gap between client and server.
We could put all of this together and entirely stop using declarative
languages on the web, even before we get into more advanced
technologies heading in our direction, like asm.js.
We can, now, build the web equivalent of mainframes: large, powerful
systems built not as a collection of disparate parts but as a cohesive
whole. We can now JavaScript all the things.
We’ve tried this before, with technologies like Java and ActiveX. And
some organizations, such as AOL, have even had success building a less
messy web-like stack. The difference this time is that the technology
available to build these “mainframes” is part of the open web stack, so
that anyone can now make their own self-contained web-like stack.
An imperative-only JavaScript web is enticing if we understand the
web as open technologies and connected documents. But if we expand our
understanding of the web to include connected systems, then declarative programming is a key part of how we connect those systems.
With that understanding, we should be heading in another direction.
Rather building more complex systems by replacing declarative
programming languages with imperative programming, we should be wrapping
more and more of our imperative code in more and better declarative
languages, so that we can build future complex systems on top of our
current work. Rather than looking at JavaScript as the modern Java or
C++, we should be treating it as the modern shell script, a powerful
tool for connecting other tools.
By defining the implementation details in the language itself,
declarative programming allows imperative languages such as JavaScript,
PHP and Ruby to use the results as steps in more complex behaviors. This
has the advantage of making a behavior available to a variety of
languages, including languages that don’t exist yet, and it also gives
us a solid foundation on which to build higher. While we could
build our own document-rendering system in JavaScript or Python, we
don’t need to because HTML has already solved that problem. And we can
reuse that solution in any imperative language, freeing us to solve new,
larger problems. JavaScript can draw an image on a canvas and place it into a document with HTML. Your friend can make you a sandwich and
a fresh lemonade. But we’ll get to this future web only by valuing
declarative programming as an approach worth maintaining, now that it’s
no longer the only option.
Declarative First
When we start building a tool on the web, we often jump right into
making it do what we want it to do. A declarative-first approach would
instead start with defining a language to succinctly describe the
results we want. Before we build a new JavaScript library for building
sandwiches (or, of course, another), let’s consider how we might describe the results in a declarative programming language. One option would look something like {"bread": "rye", "cheese": "cheddar"}, while another would look more like <sandwich><cheddar /><rye /></sandwich>.
There are many choices to make when designing a declarative language,
from high-level format choices (JSON? XML? YAML?) to details of data
structure (is cheese an attribute of a sandwich entity or an entity in
the sandwich’s toppings list?). Making these decisions early could
improve the organization of your later imperative implementation. And in
the long run, the declarative language might prove to be more important
than the amazing sandwich-making implementation, because a declarative language can be used far beyond an individual implementation.
We can see some of the advantages of a declarative-first approach in
public projects that have taken both approaches. For example, three
years ago the Sunlight Foundation started working on a project to make
it easier for people to contact members of the US Congress. They began
with a Ruby app to automate the submission of contact forms, Formageddon. This year, they launched a new declarative-first effort toward the same goal, the Contact Congress project, starting with a declarative language that describes contact forms.
The activitygraphs
and timelines of the two projects make it clear which approach won out,
but the benefits of the declarative approach go beyond the direct
results. The YAML files produced by the declarative approach can be used
to build apps like Formageddon, but they can also be used for other
purposes, ones not even intended by their creators. For example, you
could build an app to analyze the descriptions of contact forms to see
which topics members of Congress expect to hear about from their
constituents.
Successful declarative programming is in some ways more challenging
than imperative programming. It requires clear descriptions, but also
requires knowing enough about imperative implementation to know what
needs describing. You can’t just tell an app where a form is and expect a
valid submission, nor can you say <greatwebsite> to a
browser and get what you want. But if you’re up for the challenge, the
rewards of a declarative-first approach are also greater. Declarative languages often outlive their imperative implementations.
Try starting your next web project with declarative programming. See
what languages you can find that already describe what you’re making,
and write your own simple languages when you can’t find anything. Once
you have a declarative definition of what you want to make, build it
with an interpreter of those languages. You’ll probably end up making
something more useful than you would have with an imperative-first
approach to the same problem, and you’ll improve your imperative
approach in the process.
This post will help you create a new SVN repository and it assumes you have the SVN installed & configured on your computer/server.
To create the repository for first time, issue the following command
Now, you have imported your source code into the repository and then use the SVN GUI to checkin/checkout the changes.
For more detailed instruction on setting up the svn, see “http://www.rockfloat.com/howto/gentoo-subversion.html”
Before we start, I’d like to pose a question: when was the last time you asked someone to review your code? Reviewing code is possibly the single best technique to improve the overall quality of your solutions, and if you’re not actively taking advantage of it, then you’re missing out on identifying bugs and hearing suggestions that could make your code better.
None of us write 100% bug-free code all of the time, so don’t feel there’s a stigma attached to seeking help. Some of the most experienced developers in our industry, from framework authors to browser developers, regularly request reviews of their code from others; asking whether something could be tweaked should in no way be considered embarrassing. Reviews are a technique like any other and should be used where possible.
Today we’ll look at where to get your code reviewed, how to structure your requests, and what reviewers look for. I was recently asked to review some code for a new JavaScript application, and thought I’d like to share some of my feedback, because it covers some JavaScript fundamentals that are always useful to bear in mind.
[Editor's note: Have you already got your copy of the Smashing Book #2? The book shares valuable practical insight into design, usability and coding. Have a look at the contents.]
Introduction
Reviewing code goes hand in hand with maintaining strong coding standards. That said, standards don’t usually prevent logical errors or misunderstandings about the quirks of a programming language, whether it’s JavaScript, Ruby, Objective-C or something else. Even the most experienced developers can make these kinds of mistakes, and reviewing code can greatly assist with catching them.
The first reaction most of us have to criticism is to defend ourselves (or our code), and perhaps lash back. While criticism can be slightly demoralizing, think of it as a learning experience that spurs us to do better and to improve ourselves; because in many cases, once we’ve calmed down, it actually does.
Also remember that no one is obliged to provide feedback on your work, and if the comments are indeed constructive, then be grateful for the time spent offering the input.
Reviews enable us to build on the experience of others and to benefit from a second pair of eyes. And at the end of the day, they are an opportunity for us to write better code. Whether we take advantage of them is entirely our choice.
Where Can I Get My Code Reviewed?
Often the most challenging part is actually finding an experienced developer who you trust to do the review. Below are some places where you can request others to review your code (sometimes in other languages, too).
JSMentors
JSMentors is a mailing list that discusses everything to do with JavaScript (including Harmony), and a number of experienced developers are on its review panel (including JD Dalton, Angus Croll and Nicholas Zakas). These mentors might not always be readily available, but they do their best to provide useful, constructive feedback on code that’s been submitted. If you’re looking for assistance with a specific JavaScript framework beyond vanilla JavaScript, the majority of frameworks and libraries have mailing lists or forums that you can post to and that might provide a similar level of assistance.
freenode IRC
Many chat rooms here are dedicated both to discussing the JavaScript language and to requests for help or review. The most popular rooms are obviously named, and #javascript is particularly useful for generic JavaScript requests, while channels such as #jquery and #dojo are better for questions and requests related to particular libraries and frameworks.
Code Review (beta)
You would be forgiven for confusing Code Review with StackOverflow, but it’s actually a very useful, broad-spectrum, subjective tool for getting peer review of code. While on StackOverflow you might ask the question “Why isn’t my code working?,” Code Review is more suited to questions like “Why is my code so ugly?” If you still have any doubt about what it offers, I strongly recommend checking out the FAQs.
Twitter
This might sound odd, but at least half of the code that I submit for review is through social networks. Social networks work best, of course, if your code is open source, but trying them never hurts. The only thing I suggest is to ensure that the developers who you follow and interact with are experienced; a review by a developer with insufficient experience can sometimes be worse than no review at all, so be careful!
GitHub + reviewth.is
We all know that GitHub provides an excellent architecture for reviewing code. It comes with commits, file and line comments, update notifications, an easy way to track forks of gits and repositories, and more. All that’s missing is a way to actually initiate reviews. A tool called reviewth.is attempts to rectify that by giving you a post-commit hook that helps to automate this process, so changes that get posted in the wild have a clear #reviewthis hash tag, and you can tag any users who you wish to review your updates. If many of your colleagues happen to develop in the same language as you do, this set-up can work well for code reviews sourced closer to home. One workflow that works well with this (if you’re working on a team or on a collaborative project) is to perform your own work in a topic branch in a repository and then send through pull requests on that branch. Reviewers would examine the changes and commits and could then make line-by-line and file-by-file comments. You (the developer) would then take this feedback and do a destructive rebase on that topic branch, re-push it, and allow the review cycle to repeat until merging them would be acceptable.
How Should I Structure My Review Requests?
The following are some guidelines (based on experience) on how to structure your requests for code reviews, to increase the chances of them being accepted. You can be more liberal with them if the reviewer is on your team; but if the reviewer is external, then these might save you some time:
Isolate what you would like to be reviewed; ensure that it can be easily run, forked and commented; be clear about where you think improvements could be made; and, above all, be patient.
Make it as easy as possible for the reviewer to look at, demo and change your code.
Don’t submit a ZIP file of your entire website or project; very few people have the time to go through all of this. The only situation in which this would be acceptable is if your code absolutely required local testing.
Instead, isolate and reduce what you would like to be reviewed on jsFiddle, on jsbin or in a GitHub gist. This will allow the reviewer to easily fork what you’ve provided and to show changes and comments on what can be improved. If you would prefer a “diff” between your work and any changes they’ve recommended, you might also be interested in PasteBin, which supports this.
Similarly, don’t just submit a link to a page and ask them to “View source” in order to see what can be improved. On websites with a lot of scripts, this task would be challenging and lowers the chances of a reviewer agreeing to help. No one wants to work to find what you want reviewed.
Clearly indicate where you personally feel the implementation could be improved. This will help the reviewer quickly home in on what you’re most interested in having reviewed and will save them time. Many reviewers will still look at other parts of the code you’ve submitted regardless, but at least help them prioritize.
Indicate what (if any) research you’ve done into techniques for improving the code. The reviewer might very well suggest the same resources, but if they’re aware that you already know of them, then they might offer alternative suggestions (which is what you want).
If English isn’t your first language, there’s no harm in saying so. When other developers inform me of this, I know whether to keep the language in my review technical or simple.
Be patient. Some reviews take several days to get back to me, and nothing’s wrong with that. Other developers are usually busy with other projects, and someone who agrees to schedule a look at your work is being kind. Be patient, don’t spam them with reminders, and be understanding if they get delayed. Doing this sometimes pay off, because the reviewer can provide even more detailed feedback when they have more time.
What Should Code Reviews Provide?
Jonathan Betz, a former developer at Google, once said that a code review should ideally address six things:
Correctness
Does the code do everything it claims?
Complexity
Does it accomplish its goals in a straightforward way?
Consistency
Does it achieve its goals consistently?
Maintainability
Could the code be easily extended by another member of the team with a reasonable level of effort?
Scalability
Is the code written in such a way that it would work for both 100 users and 10,000? Is it optimized?
Style
Does the code adhere to a particular style guide (preferably one agreed upon by the team if the project is collaborative)?
While I agree with this list, expanding it into an action guide of what reviewers should practically aim to give developers would be useful. So, reviewers should do the following:
Provide clear comments, demonstrate knowledge, and communicate well.
Point out the shortfalls in an implementation (without being overly critical).
State why a particular approach isn’t recommended, and, if possible, refer to blog posts, gists, specifications, MDN pages and jsPerf tests to back up the statement.
Suggest alternative solutions, either in a separate runnable form or integrated in the code via a fork, so that the developer can clearly see what they did wrong.
Focus on solutions first, and style second. Suggestions on style can come later in the review, but address the fundamental problem as thoroughly as possible before paying attention to this.
Review beyond the scope of what was requested. This is entirely at the reviewer’s discretion, but if I notice issues with other aspects of a developer’s implementation, then I generally try to advise them on how those, too, might be improved. I’ve yet to receive a complaint about this, so I assume it’s not a bad thing.
Collaborative Code Reviews
Although a review by one developer can work well, an alternative approach is to bring more people into the process. This has a few distinct advantages, including reducing the load on individual reviewers and exposing more people to your implementation, which could potentially lead to more suggestions for improvements. It also allows a reviewer’s comments to be screened and corrected if they happen to make a mistake.
To assist the group, you may wish to employ a collaborative tool to allow all reviewers to simultaneously inspect and comment on your code. Luckily, a few decent ones out there are worth checking out:
Review Board
This Web-based tool is available for free under the MIT license. It integrates with Git, CVS, Mercurial, Subversion and a number of other source-control systems. Review Board can be installed on any server running Apache or lighttpd and is free for personal and commercial use.
Crucible
This tool by Australian software company Atlassian is also Web-based. It’s aimed at the enterprise and works best with distributed teams. Crucible facilitates both live review and live commenting and, like Review Board, integrates with a number of source-control tools, including Git and Subversion.
Rietveld
Like the other two, Rietveld also supports collaborative review, but it was actually written by the creator of Python, Guido van Rossum. It is designed to run on Google’s cloud service and benefits from Guido’s experience writing Mondrian, the proprietary app that Google uses internally to review its code.
Others
A number of other options for collaborative code review weren’t created for that purpose. These include CollabEdit (free and Web-based) and, my personal favorite, EtherPad (also free and Web-based).
On to the review.
A developer recently wrote in, asking me to review their code and provide some useful suggestions on how they might improve it. While I’m certainly not an expert on reviewing code (don’t let the above fool you), here are the problems and solutions that I proposed.
Problem 1
Problem: Functions and objects are passed as arguments to other functions without any type validation. Feedback: Type validation is an essential step in ensuring that you’re working only with input of a desired type. Without sanitization checks in place, you run the risk of users passing in just about anything (a string, a date, an array, etc.), which could easily break your application if you haven’t developed it defensively. For functions, you should do the following at a minimum:
Test to ensure that arguments being passed actually exist,
Do a typeof check to prevent the app from executing input that is not a valid function at all.
1
if(callback && typeofcallback === "function"){
2
/* rest of your logic */
3
}else{
4
/* not a valid function */
5
}
Unfortunately, a simple typeof check isn’t enough on its own. As Angus Croll points out in his post “Fixing the typeof operator,” you need to be aware of a number of issues with typeof checking if you’re using them for anything other than functions.
For example, typeof null returns object, which is technically incorrect. In fact, when typeof is applied to any object type that isn’t a function, it returns object, not distinguishing between Array, Date, RegEx or whatever else.
The solution is to use Object.prototype.toString to call the underlying internal property of JavaScript objects known as [[Class]], the class property of the object. Unfortunately, specialized built-in objects generally overwrite Object.prototype.toString, but you can force the generic toString function on them:
You might also find Angus’s function below useful as a more reliable alternative to typeof. Try calling betterTypeOf() against objects, arrays and other types to see what happens.
Here, parseInt() is being blindly used to parse an integer value of user input, but no base is specified. This can cause issues.
In JavaScript: The Good Parts, Douglas Crockford refers to parseInt() as being dangerous. Although you probably know that passing it a string argument returns an integer, you should also ideally specify a base or radix as the second argument, otherwise it might return unexpected output. Take the following example:
1
parseInt('20'); // returns what you expect, however…
2
parseInt('020'); // returns 16
3
parseInt('000020'); // returns 16
4
parseInt('020', 10); // returns 20 as we've specified the base to use
You’d be surprised by how many developers omit the second argument, but it happens quite regularly. Remember that your users (if permitted to freely enter numeric input) won’t necessarily follow standard number conventions (because they’re crazy!). I’ve seen 020, ,20, ;'20 and many other variations used, so do your best to parse as broad a range of inputs as possible. The following tricks to using parseInt() are occasionally better:
1
Math.floor("020"); // returns 20
2
Math.floor("0020"); //returns 20
3
Number("020"); //returns 20
4
Number("0020"); //returns 20
5
+"020"; //returns 20
Problem 2
Problem: Checks for browser-specific conditions being met are repeated throughout the code base (for example, feature detection, checks for supported ES5 features, etc.). Feedback: Ideally, your code base should be as DRY as possible, and there are some elegant solutions to this problem. For example, you might benefit from the load-time configuration pattern here (also called load-time and init-time branching). The basic idea is that you test a condition only once (when the application loads) and then access the result of that test for all subsequent checks. This pattern is commonly found in JavaScript libraries that configure themselves at load time to be optimized for a particular browser.
This pattern could be implemented as follows:
01
vartools = {
02
addMethod: null,
03
removeMethod: null
04
};
05
06
if(/* condition for native support */){
07
tools.addMethod = function(/* params */){
08
/* method logic */
09
}
10
}else{
11
/* fallback - eg. for IE */
12
tools.addMethod = function(/* */){
13
/* method logic */
14
}
15
}
The example below demonstrates how this can be used to normalize getting an XMLHttpRequest object.
01
varutils = {
02
getXHR: null
03
};
04
05
if(window.XMLHttpRequest){
06
utils.getXHR = function(){
07
returnnewXMLHttpRequest;
08
}
09
}elseif(window.ActiveXObject){
10
utils.getXHR = function(){
11
/* this has been simplified for example sakes */
12
returnnewActiveXObject(’Microsoft.XMLHTTP’);
13
}
14
}
For a great example, Stoyan Stefanov applies this to attaching and removing event listeners cross-browser, in his book JavaScript Patterns:
Problem: The native Object.prototype is being extended regularly. Feedback: Extending native types is generally frowned upon, and few (if any) popular code bases should dare to extend Object.prototype. The reality is that there is not likely a situation in which you absolutely need to extend it in this way. In addition to breaking the object-as-hash tables in JavaScript and increasing the chance of naming collisions, it’s generally considered bad practice, and modifying it should only be a last resort (this is quite different from extending your own customobject properties).
If for some reason you do end up extending the object prototype, ensure that the method doesn’t already exist, and document it so that the rest of the team is aware why it’s necessary. You can use the following code sample as a guide:
Problem: Some of the code is heavily blocking the page because it’s either waiting on processes to complete or data to load before executing anything further. Feedback: Page-blocking makes for a poor user experience, and there are a number of ways to work around it without impairing the application.
One solution is to use “deferred execution” (via promises and futures). The basic idea with promises is that, rather than issuing blocking calls for resources, you immediately return a promise for a future value that will eventually be fulfilled. This rather easily allows you to write non-blocking logic that can be run asynchronously. It is common to introduce callbacks into this equation that execute once the request completes.
I’ve written a relatively comprehensive post on this with Julian Aubourg, if you’re interested in doing this through jQuery, but it can of course be implemented with vanilla JavaScript as well.
Micro-framework Q offers a CommonJS-compatible implementation of promises and futures that is relatively comprehensive and can be used as follows:
01
/* define a promise-only delay function that resolves when a timeout completes */
02
functiondelay(ms) {
03
vardeferred = Q.defer();
04
setTimeout(deferred.resolve, ms);
05
returndeferred.promise;
06
}
07
08
/* usage of Q with the 'when' pattern to execute a callback once delay fulfils the promise */
09
Q.when(delay(500), function() {
10
/* do stuff in the callback */
11
});
If you’re looking for something more basic that can be read through, then here is Douglas Crockford’s implementation of promises:
Problem: You’re testing for explicit numeric equality of a property using the == operator, but you should probably be using === instead Feedback: As you may or may not know, the identity == operator in JavaScript is fairly liberal and considers values to be equal even if they’re of completely different types. This is due to the operator forcing a coercion of values into a single type (usually a number) prior to performing any comparison. The === operator will, however, not do this conversion, so if the two values being compared are not of the same type, then === will just return false.
The reason I recommend considering === for more specific type comparison (in this case) is that == is known to have a number of gotchas and is considered to be unreliable by many developers.
You might also be interested to know that in abstractions of the language, such as CoffeeScript, the == operator is completely dropped in favor of === beneath the hood due to the former’s unreliability.
Rather than take my word for it, see the examples below of boolean checks for equality using ==, some of which result in rather unexpected outputs.
01
3 == "3"// true
02
3 == "03"// true
03
3 == "0003"// true
04
3 == "+3"//true
05
3 == [3] //true
06
3 == (true+2) //true
07
’ \t\r\n ’ == 0 //true
08
"\t\r\n"== 0 //true
09
"\t"== 0 // true
10
"\t\n"== 0 // true
11
"\t\r"== 0 // true
12
" "== 0 // true
13
" \t"== 0 // true
14
" \ "== 0 // true
15
" \r\n\ "== 0 //true
The reason that many of the (stranger) results in this list evaluate to true is because JavaScript is a weakly typed language: it applies type coercion wherever possible. If you’re interested in learning more about why some of the expressions above evaluate to true, look at the Annotated ES5 guide, whose explanations are rather fascinating.
Back to the review. If you’re 100% certain that the values being compared cannot be interfered with by the user, then proceed with using the == operator with caution. Just remember that === covers your bases better in the event of an unexpected input.
Problem 6
Problem: An uncached array length is being used in all for loops. This is particularly bad because you’re using it when iterating through an HTMLCollection.
Here’s an example:
1
for( vari=0; i<myarray.length;i++ ){<="" code="">
2
/* do stuff */
3
}
Feedback: The problem with this approach (which I still see a number of developers using) is that the array length is unnecessarily re-accessed on each loop’s iteration. This can be very slow, especially when working with HTMLCollections (in which case, caching the length can be anywhere up to 190 times faster than repeatedly accessing it, as Nicholas C. Zakas mentions in his book High-Performance JavaScript). Below are some options for caching the array length.
01
/* cached outside loop */
02
varlen = myArray.length;
03
for( vari = 0; i < len; i++ ) {
04
}
05
06
/* cached inside loop */
07
for( vari = 0, len = myArray.length; i < len; i++ ) {
08
}
09
10
/* cached outside loop using while */
11
varlen = myArray.length;
12
while(len--) {
13
}
A jsPerf test that compares the performance benefits of caching the array length inside and outside the loop, using prefix increments, counting down and more is also available, if you would like to study which performs the best.
Problem 7
Problem: jQuery’s $.each() is being used to iterate over objects and arrays, in some cases while for is being used in others. Feedback: In jQuery, we have two ways to seamlessly iterate over objects and arrays. The generic $.each iterates over both of these types, whereas $.fn.each() iterates over a jQuery object specifically (where standard objects can be wrapped with $() should you wish to use them with the latter). While the lower-level $.each performs better than $.fn.each(), both standard JavaScript for and while loops perform much better than either, as proven by this jsPerf test. Below are some examples of loop alternatives that also perform better:
01
/* jQuery $.each */
02
$.each(a, function() {
03
e = $(this);
04
});
05
06
/* classic for loop */
07
varlen = a.length;
08
for( vari = 0; i < len; i++ ) {
09
//if this must be a jQuery object do..
10
e = $(a[i]);
11
//otherwise just e = a[i] should suffice
12
};
13
14
/* reverse for loop */
15
for( vari = a.length; i-- ) {
16
e = $(a[i]);
17
}
18
19
/* classic while loop */
20
vari = a.length;
21
while(i--) {
22
e = $(a[i]);
23
}
24
25
/* alternative while loop */
26
vari = a.length - 1;
27
28
while( e = a[i--] ) {
29
$(e)
30
};
You might find Angus Croll’s post on “Rethinking JavaScript for Loops” an interesting extension to these suggestions.
Given that this is a data-centric application with a potentially large quantity of data in each object or array, you should consider a refactor to use one of these. From a scalability perspective, you want to shave off as many milliseconds as possible from process-heavy routines, because these can build up when hundreds or thousands of elements are on the page.
Problem 8
Problem: JSON strings are being built in-memory using string concatenation. Feedback: This could be approached in more optimal ways. For example, why not use JSON.stringify(), a method that accepts a JavaScript object and returns its JSON equivalent. Objects can generally be as complex or as deeply nested as you wish, and this will almost certainly result in a simpler, shorter solution.
As an extra debugging tip, if you would like to pretty-print JSON in your console for easier reading, then the following extra arguments to stringify() will achieve this:
1
JSON.stringify({ foo: "hello", bar: "world"}, null, 4);
Problem 9
Problem: The namespacing pattern used is technically invalid. Feedback: While namespacing is implemented correctly across the rest of the application, the initial check for namespace existence is invalid. Here’s what you currently have:
1
if( !MyNamespace ) {
2
MyNamespace = { };
3
}
The problem is that !MyNamespace will throw a ReferenceError, because the MyNamespace variable was never declared. A better pattern would take advantage of boolean conversion with an inner variable declaration, as follows:
01
if( !MyNamespace ) {
02
varMyNamespace = { };
03
}
04
05
//or
06
varmyNamespace = myNamespace || {};
07
08
// Although a more efficient way of doing this is:
This could, of course, be done in numerous other ways. If you’re interested in reading about more namespacing patterns (as well as some ideas on namespace extension) Juriy Zaytsev also has a pretty comprehensive post on namespacing patterns.
Conclusion
That’s it. Reviewing code is a great way to enforce and maintain quality, correctness and consistency in coding standards at as high a level as possible. I strongly recommend that all developers give them a try in their daily projects, because they’re an excellent learning tool for both the developer and the reviewer. Until next time, try getting your code reviewed, and good luck with the rest of your project!