My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Tuesday, April 08, 2008

Famous documentation and the dark side of "this" !

The good thing of internet is that you can find a lot of free documentation.
At the same time, the bad thing of internet, is that this documentation is rarely updated.

It could be a "guru documentation" or it could be a newbie documentation, but in both cases, it doesn't matter because it is probably wrong, not updated, or too generic.

This post has not enough space to describe every error you can find on, and off line ... so let me start with some true example about a common misunderstood referer, the this one.

Douglas Crockford and Private Members in JavaScript


This page is famous enough, and I suppose that every true JavaScript developer has read them at least once.
The main error in that page is described in this sentence

By convention, we make a private that parameter. This is used to make the object available to the private methods. This is a workaround for an error in the ECMAScript Language Specification which causes this to be set incorrectly for inner functions.

We know, or maybe we don't, that everything without a prefix will be executed in the global scope or in nested closure, if any. But even if a function is created inside a method, it fortunately does not make sense to call a function that has not been assigned as instance method and find a this reference inside.

// guys, this function ...
window.testMe = function(){};

// is exactly the same of this one
function testMe(){};

// or this one in a global scope
var testMe = function(){};

The main reason we do not need to use window when we call a global method, or function, is that everything in JavaScript is virtually executed like in a global window with statement (that's why write window.open is redundant and nothing else ... and Stargate code is totally redundant too :D).

// this code ...
window.onload = function(){};

// is exactly the same of this one
onload = function(){};

onload();

// that is virtually similar to ...
with(window){
onload();
};

Let's focus on the onload example in the middle ... ok? Who will be the this referer if we do not use call or apply Function.prototype methods? window, of course.
And as window has a self property, that a link to window itself, if we use alert(this === self); it will be true every time we will use onload();
If we assign onload function as object method

onload = function(){
alert(this === self);
alert(this === window);
};
var o = {};
o.onload = onload;

... we will read two alerts with two false instead of two true value.
This simply means that to use a function that has not been assigned as method, or better, that is not called from a method, the this reference will be the global object.

onload = function(){
alert(this === window);
};
onload(); // true
var o = {onload:onload};
o.onload(); // false

function useCallback(callback){
callback();
};

useCallback(o.onload); // false again

I do not really know why Doug defined this an error in ECMA Specification, but if it really is, what could be the logic behaviour, an "unpredictable" default this referer even if the nested function is not inside an instance constructor?
I don't think so.

The intrinsic constructor Factory Design Pattern


Thanks to this "error", we could use a constructor in two ways or recognize when it has been called with new or as function.
If it is an error, it couldn't be possible to know this kind of useful information.

function Person(name, age){

// this is true only if we used
// new before Person constructor
if(this instanceof Person){
this.name = name;
this.age = age;
} else

// otherwise we used the constructor
// as factory design pattern (in this example)
return new Person(name, age);
};

// these two lines do the same thing: create a Person instance
var me = Person("Andrea", 29), // doyou like python style, don't you?
you = new Person("You", null);

alert(me instanceof Person); // true
alert(you instanceof Person); // true

Anyway, as I wrote in my recent and updated documentation, that passed totally unobserved, we do not need a that variable for nested private function.

function Person(name, age){
function setNameAndAge(){
this.name = name;
this.age = age;
};
// to call setNameAndAge as private
// method we do not need a that
// but only call or apply Function.prototype
// methods to inject instance as this referer
setNameAndAge.call(this);
};
alert(
(new Person("Andrea", 29)).name // Andrea
);

Finally, I have to admit that Douglas Crockford is my favourite JavaScript professor, and it's basically thanks to him that I know what I know about JavaScript. He wrote a lot of interesting JS stuff, and some document is truly updated, explaining for example errors wrote in old one.

My programming style has evolved since then, as any good programmer's should. I have learned to fully embrace prototypalism, and have liberated myself from the confines of the classical model.

Every programmer should evolve because in this sector knowledge is never enough. So thanks a lot Doug, but please update your 2001 doc writing something more about this, that, scope, injected this, and closures :D (a link to this post should be appreciated as well)

Ross and Dustin in Pro JavaScript Design Patterns


Differently, here we are in late 2007 ... December 2007
For some reason, few days ago someone posted again this book in Digg ... and for the first time, I read about them.

It seems to be really a good book, but without reading them, I've just downloaded examples trying to imagine what are these used for.

Fortunately, and thanks to Apress or authors decision, these sources are free and well organized inside chapter folders, each file with dedicated paragraph.

Well done ... but at the third chapter, I've read a pseudo "horror" in file 3.07 ...

var Class = (function() {

// Constants (created as private static attributes).
var UPPER_BOUND = 100;

// Privileged static method.

// ... that will never work, where is the function?
this.getUPPER_BOUND() {
return UPPER_BOUND;
}
// Return the constructor.
return function(constructorArgument) {
//
}
})();

Forgetting the missed = function, that could be a common error during quick development, getUPPER_BOUND is a privileged method of the global scope, aka window, and it is not static, it is like a window.getUPER_BOUND call, where this is obviously the window itself.
As I wrote few lines before, if we use this inside a function it will refer to window object. That's why after we can see a wrong example like this one:

var Class = (function() {
var constants = {
UPPER_BOUND: 100,
LOWER_BOUND: -100
}
// again ...
this.getConstant = function(name) {
return constants[name];
}
return function(constructorArgument) {
//...
}
})();

// anyway ... Error!!!
Class.getConstant('UPPER_BOUND');

alert(Class.getConstant); // undefined
alert(getConstant); // expected function


I am pretty sure that if this code is the one showed to explain privileged, that chapter could be full of errors ... please update them putting correct code and free corrected page inside the zip ... otherwise, sorry for this correction.

Finally, Dustin and Ross are two JavaScript ninjas, and reading the rest of the code it seems that I absolutely need to find that book because I do like design patterns and I am so curious to know how are their implementation (good stuff guys!).

Conclusion


I am the first one that writes horrible code, and probably I've even forgot what I wrote one year ago and where ... so the only suggestion I could write is that if you find something interesting, or something that you did not know, look for something else and look every time for the date that document has been published.

Have fun with self instruction, and never stop ;)

7 comments:

Anonymous said...

I have seen your documentation but haven't had time to read it yet but I will soon! ;-)

Strange to see that Dustin and Ross made such an obvious error... good of you to catch it!

Anonymous said...

There are few confusing parts in "Pro Design Patterns", e.g. these statements may easily confuse novice javascripters:

"The toString method converts a number or boolean to a string. The parseFloat and parseInt functions convert strings to numbers. Double negation casts a string or a number to a boolean: var bool = !!num;"

Andrea Giammarchi said...

kangax, I've not read the book yet ... but I agree with you about my example, and what you wrote ... the problem is: how can they upgrade book errors? :)

Anonymous said...

Well, they have erratas for things like that. The example I gave, on the other hand, is not exactly a technical error. They have just explained typecasting in a somewhat misleading way. Maybe I'm just being picky, after all : )

进行时 said...

It's really strange that Constants is coded that way, which does not work. Then how do you suppose to fix that issue? Or is it possible we implement a Privileged Static method in JavaScript?

Brett said...

The situation can be solved like this:

var Class = (function() {
var constants = {
UPPER_BOUND: 100,
LOWER_BOUND: -100
};
function getConstant (name) {
return constants[name];
}
var constructor = function () {
// Put class constructor code here
};
constructor.getConstant = getConstant;
return constructor;
})();

alert( Class.getConstant('UPPER_BOUND') ); // '100'

Or to solve their simpler example in the book previous to this one:

var Class = (function() {
var UPPER_BOUND = 100;

function getUPPER_BOUND (name) {
return UPPER_BOUND;
}
var constructor = function () {
// Put class constructor code here
};
constructor.getUPPER_BOUND = getUPPER_BOUND;
return constructor;
})();

alert( Class.getUPPER_BOUND() ); // '100'

You could also do it like the following, but then it'd be more work to access the constants from within Constructor() or its prototype, since you'd have to hard-code the class name. One advantage though might be being able to more easily attach to an existing constructor:

function Constructor () {
}
Constructor.getConstants = (function () {
var constants = {
UPPER_BOUND: 100,
LOWER_BOUND: -100
};
return function (name) {return constants[name];};
})();
alert(Constructor.getConstants('UPPER_BOUND'));

The JS Pro Design Patterns book is exemplary in teaching lots of cool approaches, though there are a number of errors, as might be expected in a first edition (I don't know any technical book which doesn't have quite a few errors of some kind--a pity publishers don't pay bounties for errata or they could actually improve rather quickly).

Andrea Giammarchi said...

I don't know any technical book which doesn't have quite a few errors of some kind
you just have to wait the next JS book where I will be the technical supervisor :D
(seriously, it is happening, just be patience please!)