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:

  1. 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?

    ReplyDelete
  2. 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.

    ReplyDelete
  3. 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.

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

    ReplyDelete
  5. 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.

    ReplyDelete

Note: Only a member of this blog may post a comment.