So for the past few months I’ve narrowed down the projects that I’ve allowed myself to work on to two. Which probably isn’t really narrowing down enough, but one of the things I’m learning about myself is that a series of small steps with a specific outcome is an effective way of progressing.
Both these projects – Fracas, which I’ve mentioned here and a currently nameless one (I grew tired of the previous name and am not entirely pleased with the working name) which I have yet to but is all over my personal blog archives – are quite heavily reliant on JavaScript.
I am coming to the realization that JavaScript is my language of choice for the next phase of my career. I have tried writing this paragraph half a dozen times now and that seems to be the best phrasing of the statement I am trying to make. I think JavaScript is coming into its own and will be an extremely important language in the coming decade, but mostly I’m just into it in a way that I have felt the absence of in languages thus far. I want to do more than just program with it, I want to understand it.
I’ve been using JavaScript, with varying degrees of regularity since about 1998, but while working on Fracas and the unnamed project I’ve been extremely frustrated about my lack of understanding of modern JavaScript syntax and how it works and how libraries and tools built in JavaScript (specifically jQuery but also other projects like Node.js) work.
I finally over the past little bit decided to really start doing something about it and finally, after all these years, managed to stumble onto Douglas Crockford. Technically I should have known his name long ago since he is the inventor of JSON but I guess I’ve just read the specification itself a dozen times without paying attention to how it became one.
Anyway, Mr. Crockford knows his JavaScript and he knows how to explain it. Last night I read Code Conventions for the JavaScript Programming Language and immediately understood several important details that had been confounding me to no end. Crockford recently presented a 5-part lecture series on JavaScript that is a great watch.
What follows is an extremely heavily edited version of jQuery, with the vast majority of its guts ripped out leaving only a few bits and pieces to illustrate the places I was getting confused. If you were to paste it into script tags it would cause errors since jQuery.each is not defined, so it’s not actually executable just for looking at.
(function( window, undefined ) {
// Define a local copy of jQuery
var jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context );
},
// Map over jQuery in case of overwrite
_jQuery = window.jQuery,
// ... snipped a bunch...
indexOf = Array.prototype.indexOf;
jQuery.fn = jQuery.prototype = {
init: function( selector, context ) {
var match, elem, ret, doc;
// ... snipped a bunch ...
},
// Start with an empty selector
selector: "",
// ... snipped a bunch ...
sort: [].sort,
splice: [].splice
};
// ... snipped a WHOLE bunch ...
// Create innerHeight, innerWidth, outerHeight and outerWidth methods
jQuery.each([ "Height", "Width" ], function( i, name ) {
// ... snipped a bunch ...
});
// Expose jQuery to the global object
window.jQuery = window.$ = jQuery;
})(window);
This framework, within which all of jQuery is defined, just baffled the shit out of me primarily due to lack of understanding of conventions.
Parentheses in JavaScript, like most C-style languages, are used as part of control structures and function declarations are vital there. But they’re also a completely trivial part of normal statements used for clarity – like when grouping parts of a mathematical statement as in (1 + 2) + (3 + 4). So in this jQuery code the whole thing is wrapped in parentheses, followed by another pair of parentheses containing ‘window’:
(function( window, undefined ) {
})(window);
That second pair of parentheses I understood, it called the anonymous function immediately passing it window as a parameter but why that first pair? Well, Mr. Crockford clears it up for me:
When a function is to be invoked immediately, the entire invocation expression should be wrapped in parens so that it is clear that the value being produced is the result of the function and not the function itself.
Oh. Cool!
I have a couple of guesses why window and undefined are being passed. The former probably ensures that the global window object is what’s actually being used inside of jQuery, and the latter seems like a convenient way to create something to use to test if variables have been defined. But this whole paragraph is speculation, I’m really not entirely sure about it just yet.
The next convention I was unaware of is illustrated by this block:
// Define a local copy of jQuery
var jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context );
},
// Map over jQuery in case of overwrite
_jQuery = window.jQuery,
// ... snipped a bunch...
indexOf = Array.prototype.indexOf;
It’s more easily illustrated later in the source with the line:
var match, elem, ret, doc;
It’s a simple convention, common in most languages, and is simply the declaration of multiple locally-scoped variables. For some reason I was conflating the use in jQuery (and other scripts I’ve seen in recent months) with an object literal, likely because I am only just on the cusp of understanding them, too. I’m getting there though.
Only two more things, which is quite a bit for a small block of code. The first of which is interesting to me because it’s completely novel to me and fairly illustrative of why JavaScript fascinates me so much.
var jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context );
}
// ... snip snip snip
jQuery.fn = jQuery.prototype = {
init: function( selector, context ) {
var match, elem, ret, doc;
},
// ... snip snip snip
};
So, an object named jQuery is created. The way JavaScript works is that the object declaration is also its constructor (my terminology is likely weak, I would love to be corrected), so in this case the only thing that the object is is a call to a member function that doesn’t yet exist. Shortly afterwards, it’s defined.
Why jQuery uses jQuery.fn for instead of jQuery.prototype I haven’t yet got around to understanding but they do. The second bit above assigns an object literal to the prototype which contains the actual init function. Again, I’m not entirely sure why this particular behaviour is used (is it convention or to work around an issue or maybe it’s just Resig’s style?) and look forward to that next level of comprehension.
// Create innerHeight, innerWidth, outerHeight and outerWidth methods
jQuery.each([ "Height", "Width" ], function( i, name ) {
// ... snipped a bunch ...
});
So this last bit, the part that will throw errors, is only to point out how little it takes to lose me. See, the end of this statement (});) comes right at the end of the file, some 6200 lines after the start. It’s also a sequence of characters that is usually used at the end of an anonymous function passed as a parameter to another function (callingFunction( function(){/*stuff*/});) and since I’d assumed that all of jQuery was defined as an anonymous function (no, I don’t know why I’d assumed that, outside of the same lack of understanding as all along) which was easy to clear up in the end but combined with everything else I wasn’t getting caused a couple of small bumps in comprehension.
I’m at the point where I’m starting to get the how, at a slightly deeper level than before, but I’m still only just scratching the surface of the why.