Thursday, December 3, 2009

with: Some Good Example

OK, I have said my last post would have been the last one about the with statement ... well, before I close the argument, I would like to show a couple of common good examples.

Solve References Problem

This is what somebody defined
a tiny masterpiece


with({o:myreference}){
o.doStuff();
o.var1 = "whatever";
// etc etc ...
};

Above example annihilate every blame about the not sure if that is the var I meant to use since there is an explicit reference as is, as example, in Python programming language (until version 3.11)

// Python
with open("x.txt") as f:
data = f.read()

// JavaScript equivalent
with({f:/* as */open("x.txt")})
data = f.read()


Memory Safe Operations

Another recycled example before I show more interesting stuff ... apparently the whole problem is about write silly code inside with, as if everybody has to define variables or assign stuff inside this statement. Hilariously, I have basically never assigned anything inside a with in 8 years of ECMAScript programming ... does it tell you anything?
A clever usage could be the one to beat whatever compressor and make code less redundant:

with(document)
with(documentElement)
insertBefore(
createElement("script"),
firstChild
)
.text = "alert(1)"
;

Try to do more in less characters considering I am not creating a single reference at all, as I am not creating a useless closure just to justify references creation ... done? Now use whatever compiler/compresor/minifier you want and try to obtain less than 99 bytes, respecting the same cross browser, clean, memory safe and leaks free nature ...

Discover Named Arguments

A too much common approach in JavaScript is the one to create an inline executed closure to perform some task being sure local variables won't disturb outer or global scope.

(function(obj, collection, callback){
// a common argument normalizer
if(!collection)
collection = []
;
// a loop, 'cause we don't want to perform it
// in the global scope
for(var i = 0, length = collection.length, tmp; i < length; ++i){
// callback returns an object only under certain conditions
tmp = callback.call(obj, collection[i]);
if(tmp){
tmp.doStuff();
collection[i] = tmp;
};
};
})();

Guys, above example is daily basis JavaScript programming
  1. create a closure to avoid outer scope properties declaration

  2. normalize, if necessary, unexpected/undefined/empty arguments

  3. define internal closure variables with or without a value

  4. perform some operation

  5. exit from the executed closure (implicit)
Now, consider we all love to call methods or functions via configuration objects

ajax({
url:somestring,
params:someObject,
success:callback,
failure:shenanigans
});

A configuration object is semantic, it allows us to perform some task inline, like ternary operator assignment (and I bet since somebody does not know ternary operator ES5 team will decide it's evil ...) and make callback calls more friendly ... but what we have always been envious about Python is the ability to use named arguments defining defaults, if necessary, for each value.
In JavaScript we can send named arguments, via a configuration object, but we cannot define arguments defaults ... but in one shot, we could emulate everything defining both defaults and, since we need to define them in any case, local variables ... don't you follow me?

// zero closure named arguments example
with({
obj:myObject,
// inline defaults, if necessary
collection:collection || [],
callback:myCaseAnalyzer,
// local variables
i:0,
length:collection ? collection.length : 0,
// local undefined variable
tmp:null
}){
for(;i < length; ++i){
tmp = callback.call(obj, collection[i]);
if(tmp){
tmp.doStuff();
collection[i] = tmp;
};
};
};

Same behavior, zero ambiguity, if we call another variable name is because we need that variable from the outer scope. If we create a variable we are doing wrong because inside a closure we would have used var, while with this approach we need simply to define, rather than var, a property with null value.
Moreover, if tomorrow we need an extra argument via with statement all we need to do is to define it in whatever position we like, without being worried about arguments order (let's say bad design) since there is no order, except the one we prefer, in object properties.

Global Clear Ajax Call

This is the last example for this post, just ajax when we need it, whenever we are, without the classic reference and the missed "this" feature in the onreadystatechange function:

with(this.XMLHttpRequest ?
new XMLHttpRequest :
new ActiveXObject("Microsoft.XMLHTTP")
){
open("get", "?ajax=true", true);
onreadystatechange = function(){
if(readyState === 4)
// do stuff with the responseText/XML
alert(responseText)
;
};
send(null);
};


Global Paradox

The funniest part ever about this with statement is that basically every JavaScript has an implicit global object scope.
In few words every time a script is executed is basically the same of a massive with

with(window){
// same as window.alert(123);
alert(123);

// same as window.onload = function(){};
onload = function(){
};

(function(){
// same as new window.XMLHttpRequest
var xhr = new XMLHttpRequest;
})();

// same as window.String.fromCharCode
String.fromCharCode(1);
}

Of course being the global object the last possible outerscope, if we define a variable directly or via var in that scope we will attach this property to the window one.
In few words the with is the ABC of JavaScript but again, some clever guy decided that since somebody cannot understand closures, then extended closures, the basic principle of the programming language itself has to be removed ... right?

Think about it, if ES5 will remove even the with statement making this unavailable in the "use strict" future proof declaration, JavaScript won't be the one that created all its success around its closure and prototype based nature.

Think!

No comments:

Post a Comment