My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.
Showing posts with label this. Show all posts
Showing posts with label this. Show all posts

Sunday, October 11, 2009

ES5 and function default scope

This is just a quick one. One of the worst decision about ES5, after the one to remove arguments.callee when "use strict" is present, is the missed default reference to the global object. Probably the most secure trick to retrieve the native window in JavaScript is this one:

// wherever we are
var global = function(){return this}();

// while window, could be simply a variable
// assigned somewhere else in this scope


I have already talked about this, and I am still against some ES5 "feature" which aim is probably the one to make JavaScript as fast as possible, regardless all code that will break if "use strict" is present in the generic scope.

Another non-sense is to allow a this in a context where there is no this. Rather than an error, ES5 will point refer this to undefined.
Are you kidding me? this is a special keywords related to the context and you are telling me a context is undefined? so I guess open(), as example, should throw an error there, since window cannot be implicit in an undefined context, isn't it? ... anyway, fortunately the whole problem could be quickly solved in this way:

// ES5 problem
(function(){
"use strict";

this === undefined; // true ... LOL!!!

})();


// WebReflection QAD solution
(function(){
"use strict";

this === window; // true ... yeah baby!

}).call(this);

This simple modification could avoid lots of headaches plus could avoid scope resolution for every window variable.

It's probably quite obvious but I can already imagine lots of people changing the whole function body rather than add a call at the end :D

The trick to retrieve the global context won't work in any case for nested functions, unless we are not sure that this points to window, but being the trick goal the one to make window resolution quick and safe, there is not window,self,top, whatever check that could solve the problem (unless [].sort() will be still buggy ...)

As summary, the day JavaScript will be that fast thanks to removed dynamic and run-time features, it won't be JavaScript anymore but another language a la ActionScript 2 when ActionScript 1 was enough, and the problem was the player rather than the language, which indeed has been replaced by ActionScript 3 even if the problem is still the player and not the language, since it can be compilable into old versions.

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 ;)