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

Friday, October 23, 2009

JavaScript For In Madness

Have you never instinctively written a for in loop like the next one, instantly correcting it since you need a variable to perform it?

for(var k in {oops:null}){
// cool, got "k" here ...
// now how do I get
// the bloody "oops" value?
}

This quick post talks about above nonsense case providing a solution I will explain only if anybody will be interested about it, since (I am quite lazy and tired right now and ...) I think I've been able to solve the trick using almost every JavaScript bad practice :D

Runtime Assigned For In Loop



(function(self, Function){
// Another (C) WebReflection Silly Idea
"use strict"; // Mit Style Weirdness
var $o;
self.o = function o($){
if(!$)
self.o = $o;
else {
$o = self.o;
self.o = Function(
"$", "return function o(" +
new Array(($.length || 0) + 1).join(",k").slice(1) +
"){return $(arguments[0])}"
)(o);
for(var k in $)
self.o[k] = $[k]
;
};
return $;
};
})(this, Function);


WTF Is That

Above agglomerate of dynamic assignments/compositions let us perform a forIn without creating (virtually speaking) a variable. How?

for(var k in o({a:"b", c:"d"})){
alert([k, o[k]]);
};

In few words we don't need to define the "o" object in any place. The trick works as expected with blocked properties, as length is for a function, name, and whatever else was assigned to the object and even nested

for(var k in o({
length:123,
name:"test"
})){

alert([k, o[k]]);

// nested for in ...
for(var k in o({a:"b"}))
alert([k, o[k]])
;

o(); // back in town parent object

alert(o.name); // test
};

Isn't it cool? Even with Arrays!

for(var k in o([1, 2, 3])){
alert([k, o[k]]);
// 0,1
// 1,2
// 2,3
};

The good part is that break will work as expected and the changed object is quite static since it is a function, looped over original object properties.

Why This Abomination

Well, let's say with let and var we can do what we want in a generic for loop but we are kinda stuck with a single variable with a "for in" one.
What would be truly nice in my opinion is the possibility to implement this kind of syntax:

for(var o = {a:"b"}, k; k in o){
// common loop with o and o[k]
};

Something similar to PHP world where we have:

foreach(array('a'=>'b') as $key => $value){
// do stuff and forget about the config variable
}

I am pretty much sure JavaScript will never implement above syntax but hey, that's why I have created a monster I'll probably never use in my programming life :P

The main trick about supported length property is described in this post.

6 comments:

Nicolas said...

Nice post :)

A couple of questions...

Does your implementation works with n-depth nested levels? I kind of tested it with this example and it doesn't seem to work:

for(var k in o({
length:123,
name:"test"
})){

alert([k, o[k]]);

// nested for in ...
for(var k in o({a:"b"})) {
alert([k, o[k]]);

for(var kk in o({'newkey':50})) {
alert([kk, o[kk]]);
}

o();
alert(["a", o.a]);
}
o();
alert(["len", o.length]);
};

I modified just a little bit the code and it seemed to work fine like this:

(function(self, Function){
// Another (C) WebReflection Silly Idea
"use strict"; // Mit Style Weirdness
var $o = [];
self.o = function o($){
if(!$)
self.o = $o.pop();
else {
$o.push(self.o);
self.o = Function(
"$", "return function o(" +
new Array(($.length || 0) + 1).join(",k").slice(1) +
"){return $(arguments[0])}"
)(o);
for(var k in $)
self.o[k] = $[k]
;
};
return $;
};
})(this, Function);

...but I'm not sure it's right.

Also, I kind of had a couple of problems with the "name" function property in the example. Does this have to do with the fact that "name" is a reserved property for Function objects?

Jani Hartikainen said...

Wow, that is some crazy stuff. I guess this is what you need the ECMAScript certification for... :D

I would not want to see that monstrosity in any production code though.

Andrea Giammarchi said...

me neither ;)

Josh Powell said...

Weeeeee! Thanks for the challenge Andrea, that was a fun lunch time effort for me.

for(var k in a = {oops:null}){

console.log(k);
console.log(a[k]);

}


Ok, I only tested it in Firefox, Opera, and Safari so it may barf in ie.

Andrea Giammarchi said...

Josh, you have created a global "a" object in that way :-)

Josh Powell said...

Very true, but it didn't let me scope it with a var in front.

// This doesn't work.
for(var k in var a = {oops:null}){
console.log(k);
console.log(a[k]);
}

I guess the way around that is
var a = {oops:null};
for (var k in a) {

}

But that doesn't let you have a one line control statement.