Created
August 12, 2011 07:35
-
-
Save uhop/1141652 to your computer and use it in GitHub Desktop.
dojo/array.js: forEach
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Benchmark it with http://www.perfjs.com/