Skip to content

Instantly share code, notes, and snippets.

@uhop
Created August 12, 2011 07:35
Show Gist options
  • Save uhop/1141652 to your computer and use it in GitHub Desktop.
Save uhop/1141652 to your computer and use it in GitHub Desktop.
dojo/array.js: forEach
// dojo/array.js: forEach
/*
LEGEND:
old --- the previous implementation from Dojo trunk.
nnn --- the new implementation.
ech --- https://github.com/kriszyp/each
NOTES:
All functions are copied below together with all necessary utility functions.
All tests are performed with and without the context (thisObject).
An iterating function is empty to measure pure iteration expenses.
*/
var a = noise(1);
this.group(
"1 no ctx",
function old(){ dojo_forEach(a, function(){}); },
function nnn(){ forEach(a, function(){}); },
function ech(){ each(a, function(){}); }
);
this.group(
"1 w/ ctx",
function old(){ dojo_forEach(a, function(){}, a); },
function nnn(){ forEach(a, function(){}, a); },
function ech(){ each(a, function(){}, a); }
);
var b = noise(10);
this.group(
"10 no ctx",
function old(){ dojo_forEach(b, function(){}); },
function nnn(){ forEach(b, function(){}); },
function ech(){ each(b, function(){}); }
);
this.group(
"10 w/ ctx",
function old(){ dojo_forEach(b, function(){}, b); },
function nnn(){ forEach(b, function(){}, b); },
function ech(){ each(b, function(){}, b); }
);
var c = noise(100);
this.group(
"100 no ctx",
function old(){ dojo_forEach(c, function(){}); },
function nnn(){ forEach(c, function(){}); },
function ech(){ each(c, function(){}); }
);
this.group(
"100 w/ ctx",
function old(){ dojo_forEach(c, function(){}, c); },
function nnn(){ forEach(c, function(){}, c); },
function ech(){ each(c, function(){}, c); }
);
var d = noise(1000);
this.group(
"1000 no ctx",
function old(){ dojo_forEach(d, function(){}); },
function nnn(){ forEach(d, function(){}); },
function ech(){ each(d, function(){}); }
);
this.group(
"1000 w/ ctx",
function old(){ dojo_forEach(d, function(){}, d); },
function nnn(){ forEach(d, function(){}, d); },
function ech(){ each(d, function(){}, d); }
);
function noise(count){
var a = new Array(count);
for(var i = 0; i < count; ++i){
a[i] = Math.random();
}
return a;
}
// old
function _getParts(arr, obj, cb){
return [
(typeof arr == "string") ? arr.split("") : arr,
obj || dojo.global,
(typeof cb == "string") ? new Function("item", "index", "array", cb) : cb
];
}
function dojo_forEach(/*Array|String*/arr, /*Function|String*/callback, /*Object?*/thisObject){
// match the behavior of the built-in forEach WRT empty arrs
if(!arr || !arr.length){ return; }
// FIXME: there are several ways of handilng thisObject. Is
// dojo.global always the default context?
var _p = _getParts(arr, thisObject, callback); arr = _p[0];
for(var i=0,l=arr.length; i<l; ++i){
_p[2].call(_p[1], arr[i], i, arr);
}
}
// new
var cache = {}, u;
function buildFn(fn){
return cache[fn] = new Function("item", "index", "array", fn);
}
function forEach(a, fn, o){
var i = 0, l = a && a.length || 0;
if(l && typeof a == "string") a = a.split("");
if(typeof fn == "string") fn = cache[fn] || buildFn(fn);
if(o){
for(; i < l; ++i){
fn.call(o, a[i], i, a);
}
}else{
for(; i < l; ++i){
fn(a[i], i, a);
}
}
}
// each
function getEmit(result){
return function(newValue){
// emit function adds result to return array
result.push(newValue);
};
}
function each(array, callback, thisObject){
// create an emit function if there is enough arguments, otherwise avoid the allocation cost
var i = 0, result, emit, length = array.length;
if(typeof callback == "function"){
// standard each usage, calling a callback on each item
if(callback.length > 2){
emit = getEmit(result = []);
}
if(length > -1){
if(thisObject){
// iterate over array
for(;i < length; i++){
// call the callback
var newValue = callback.call(thisObject, array[i], i, emit);
// if a value was returned, examine it
if(newValue !== undefined){
// defined value breaks out of loop
return newValue;
}
}
}else{
// we do a separate branch for when thisObject isn't provided because
// it is faster to avoid the .call()
for(;i < length; i++){
// call the callback
var newValue = callback(array[i], i, emit);
// if a value was returned, examine it
if(newValue !== undefined){
// defined value breaks out of loop
return newValue;
}
}
}
}else{
// not an array, iterate over an object
for(i in array){
if(array.hasOwnProperty(i)){
var newValue = callback.call(thisObject, array[i], i, emit);
}
if(newValue !== undefined){
// defined value breaks out of loop
return newValue;
}
}
}
return result;
}
// indexOf operation
for(i = thisObject || 0; i < length; i++){
if(array[i] === callback){
return i;
}
}
return -1;
}
@uhop
Copy link
Author

uhop commented Aug 12, 2011

Benchmark it with http://www.perfjs.com/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment