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

Wednesday, January 24, 2007

bored with something to array convertion ? map them !

I'm rewriting my JSL to include by default inside my next project.
This project will include automatically a lot of standard JS 1.5 methods or functions, and Array.map is one of those.

While I'm testing some proto performances, I've thought about a really simple way to switch from an array or from a node list (getElementsByTagName) with a simple, fast and single line of function.

// old example version
// function _A(a){return [].map.call(a,function(a){return a})};

// new version, Daniel suggest, faster and doesn't require any prototype !
function _A(a){return [].slice.call(a)};

and that's all, do You like it ?

As first point, You need FireFox 1 or greater (or Mozilla if You prefere) or this short piece of code:

if(!Array.prototype.map) Array.prototype.map = function(callback){
for(var i = 0, j = this.length, result = new Array(j), self = arguments[1], u; i < j; i++) {
if(this[i] !== u)
result[i] = callback.call(self, this[i], i, this);
};
return result;
};


Then You have "everything You need" to get quickly every iterable element.

These are just two examples

function sort(){
return _A(arguments).sort().join("<br />");
};
document.writeln(sort("Luke", "Jabba", "Fenner"));
document.writeln(sort("c", "a", "b"));



var firstScript = _A(document.getElementsByTagName("script")).shift();
alert(firstScript);

var allUnorderedList = _A(document.getElementsByTagName("ul"));

alert(_A(null).constructor === Array); // true ;-)


These nice trick should be used with forEach method too.
Stop for and while loops, each them !

function changeLinks(onclick){
[].forEach.apply(document.getElementsByTagName("a"),
function(link){
link.onclick = onclick;
});
};

changeLinks(function(){
window.open(this.href, "");
return false;
});


... and sure, You need forEach prototype too ...

I suppose every other JS 1.5 Array method should be cool enought to work with DOM nodes too ... do You agree ?

Are thinking about performances ? Well guys, quite the same of generic loops, even faster (works in core) with every FireFox !!!

3 comments:

Daniel said...

You can use slice to create arrays. I think 'Array.prototype.slice.call(a)' works for node lists on Firefox and Opera 9, but not Safari (I can't remember if it works on Internet Explorer). So I would sniff the browser and the version and only use it when 100% sure that it is supported.

Andrea Giammarchi said...

sure Daniel, You can use every type of array prototype and slice should be even better (suported on IE4 too)

function _A(a){return [].slice.call(a)};

why not ... it's cool but with map, forEach or filter You should use directly a callback to parse every object inside each arguments (and with map prototype You'll be shure browser will support them witho nodeLists too ;-))

However, how many libraries use this "tips" ? ... look inside prototype code, for example, You'll see a for loop while $A function should be:
function $A(i){return i&&i.toArray?i.toArray():[].slice.call(i)};

Do You agree ? :-)

Andrea Giammarchi said...

Well ... seems that IE doesn't accept this trick with document nodes ... strange, uh ?