As a guy that tries to live his professional life in the middle of the dichotomy “C/C++ - JavaScript”, I often find myself discussing the discrepancies, differences and implementation details of the latter (an half-assed prototypal language) using the former.
The other day I was chatting with Luca (@lucabox) about scope in JS, and we were describing the different situations you can end up with… and how we work our way out of those culprits.
What I did later on, was picking up JS-Bible and go to Chapter 4, Functions. I strongly advice you get yourself one and read that chapter again and again, as I did now for the 3rd time. Every time there is a little bit of news to grow your knowledge. That book has to stay on your desk, at any time.
After that I did the obvious: git checkout master && qmake && make my beloved PhantomJS and started fiddling around.
In Chapter 4 of Crockford’s there is a section called “Invocation”: there The Man highlights the 4 Invocation Patterns that can be used in JavaScript. What I wrote was a practical demonstration of those patterns: this way it would be an easy documentation of how functional scoping is such a tricky bitch to handle.
The “subject”
First, I wrote this function:
| |
What this those is reading the value of aVariable, taking it implicitly from the current scope and explicitly from this (i.e. pointer to the current scope) and ** from window** (i.e. the gloval scope). Also, it does a couple of extra bits:
- Checks if there is
that.aVariablein the current scope (in case we are using the famousthat = thistrick) - Highlights when the magical JS Runtime is using the global scope (
window) as local scope (this)
Yes, and I added some bash colouring to my print-out: I like to have the key stuff highlighted to me.
Function Invocation Pattern
I started with something very easy:
| |
that will print out:
| |
What’s happening here is obvious: the scope in which the invocation is executed, is parent of the internal scope of the callee. Hence, if a variable is not found in the local scope, will be looked-up in the parent scopes(s).
But we all knew that, didn’t we?
Method Invocation Pattern
Now, let’s start to have some fun:
| |
the above will print:
| |
The object obj to which we added a reference to foo, now acts as the scope parenting the internal function scope. Hence, aVariable is found in obj, set to something different. In other words, during the invocation the function run in a scope where obj === this.
Constructor Invocation Pattern + Prototype Inheritance
| |
predictably this prints:
| |
when I was working at Betfair, I found this kind of inheritance in the hierarchy we used, and all was based on YUI2. Than moved to more Closure-based inheritances with YUI3.
It’s not a bad pattern per se, and makes it pretty clear to understand what’s going on, but it does not offer the protection of Closures.
Constructor Invocation Pattern + Closure
Here I have to take a bit of “liberty” and make some ugly detour:
| |
that prints out:
| |
In the first invocation (i.e. obj.fooOriginal(...)) the callee acts as expected: same printout as per the Prototype Inheritance. But when invoked by obj.fooWrapper(...) obj’s scope is not made parent of the callee scope, as it is invoked as-is (i.e. foo(m);).
A Closure though was indeed created: console.log(aVariable + " (from the wrapper)"); proves just that.
Apply Invocation Pattern
| |
obviously produces:
| |
this is the easiest one, as the scope manipulation is made very explicit, and controlled by the developer. Useful when you want to tightly control what your callee receives.
var that = this Invocation Pattern
If you wrote a lot of JavaScript, you have written one of this. Somewhere in time, you had a piece of code that drove you nuts for a little while, because the scope was just like Frank Abagnale. Hence, to trap it, you created a fixed reference to it, an “alias” some would say.
| |
and this one comes out with:
| |
what’s the reason? it’s the only way to “catch and hold” this, the scope. that is than a reference to this, to ensure the callee can still get hold of it. And yes, this is a Closure.
What do you say? Too messy? Well, the that = this-for-dummies is:
| |
but the same applies.
Conclusions
Well, after all this the only real conclusion I can give you is: be careful when writing JavaScript, and make sure you really understand what’s going on.
Tools like PhantomJS, Firebug, WebKit Inspector or Opera Dragonfly (choose your favourite), are your best friends to workout what’s going on. When your code grows, it’s not always YOUR code: breakpointing and analysing in details what’s going on it’s often the only way to solve this kind of issues.
And you, tweeps: please share your wisdom. I’m sure you guys will have loads to say about this ;-) .
Happy scoping!
UPDATE: here is the full gist invocation_patterns.js.