Sunday, April 26, 2015

Crockford's Concoction

Having worked through HTML and CSS (and many related frameworks/preprocessors), my cohort and I have arrived at what will presumably take up most of our class time going forward (since our backend framework is Node.JS): Javascript.

I just finished reading and doing all the exercises for A Smarter Way to Learn Javascript, so "document.getElementById()" and "function thisArbitraryFunction() {" are pretty well committed to muscle memory. I know that eyeballs are the only body parts that are supposed to move during REM sleep, but I wouldn't be surprised if my fingers are twitching those commands out during my dreams to come...

Anyway! How about some content?

As part of our "homework" we have been watching Douglas Crockford's "Crockford on Javascript" lecture series on Youtube. The first is a fascinating look through computer and programming language history up until Javascript, the second touches on the particulars and history of Javascript itself, while the third outlines strengths/peculiarities of what Crockford considers Javascript's greatest asset: its functions.

I'd like to consider some code presented toward the end of the lecture, which Crockford presents as a common mistake made by Javascript developers:


Crockford points out that in the top code, all event handlers created by the loop will report the same div_id (the last one) no matter which one is clicked, which is not the desired behavior. In the bottom code, however, each div_id is reported (alerted) correctly because the "make_handler" function is pulled outside of the loop. He tells us that this fixes the problem because of function closure.

Hmm.. I had to pause for a while and give this one some thought. Here's how I see it:

In the top loop, we've created some arbitrary number of event handlers. The loop makes them all at once, and after their respective geneses they all do their thing, handling events as event handlers are wont to do. Each handler is supposed to listen for clicks on a specific div, and alert their specific div when it's clicked.

Great, except that the "div_id = divs[i].id;" line occurs outside of the actual "divs[i].onclick = function () {" functions that are created. In other words, div_id exists outside of the generated functions' closures; like Python (and unlike Ruby unless you explicitly make a global or instance variable, afaik), functions are able to pull variables "above" them in scope in to use, but variables that are declared/reassigned within them are scoped to their respective function closures.

So what happens here is that we get event handler functions that each handle click events for their particular divs. They do this job admirably, but when the actual functions are called, they each alert a "div_id" variable that is pulled in into the function's scope from outside of it; "div_id" does not belong to the function's scope; it belongs to the scope that sits one level above each function.

In other words, each function pulls, from outside of its scope, the same "div_id" variable! And this "div_id" will point to the very last div that the iterator "i" iterates through in the "for" loop, since that's the last value that div_id is set to before the loop ends and we stop reassigning it. The loop runs its course, all the event handlers are created, and div_id winds up set to the very last value of divs[i]. Each individual event handler function now reaches outside of its scope to pull in and alert this same div_id variable, no matter which div the handler is actually handling. Div clicks are handled, but when they are handled, each handler says that it's the last div.

Which is no good!

The bottom code alleviates this by bringing the div_id variable inside of make_handler's closure before passing it to the event handler function that is created and returned. This new event handler function reports a div_id variable that is scoped to its progenitor make_handler function. So each event handler function no longer pulls in the same div_id from outside; it instead pulls in the div_id that exists within its parent make_handler function's closure. Since make_handler was called with a different div_id each time in order to create each event handler function, each event handler function now pulls in its own unique div_id, which it will dutifully report when clicked.

So, thanks to judicious use of function closure, each event handler function now reports its own div_id, all of which are different despite having exactly the same variable name.

I hope that does a decent job of explaining it! It was a lot of fun to work through mentally.


No comments:

Post a Comment