Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

added "user strict"; moved the polyfill to its own namespace, ran JsL…

…int and made some fixes, trimmed the minified library size to 2.3k."
  • Loading branch information...
commit aa3544f8d47af57ed119dbaaf73ed47f983a5c62 1 parent d2a25ad
@olooney authored
Showing with 110 additions and 97 deletions.
  1. +106 −79 attache.array.js
  2. +1 −14 attache.array.min.js
  3. +3 −4 test/attache.array.test.js
View
185 attache.array.js
@@ -1,56 +1,72 @@
// The author disclaims copyright to this code.
(function() {
- // Convert any object to a non-negative integer.
- // Negative lengths are a sticking point. The standard implies that
- // you should execute 2^31 operations if you get a negative length, but
- // that seems like a mistake to me. Most browsers don't if you pass in
- // a negative length, although some "correctly" hang for at least some
- // of the algorithms. Regardless, the only reasonable thing to do with
- // a negative length is to return immediately.
- function ToUint32(n) {
+ "use strict";
+
+ // Short Synonyms
+ // Throughout this library:
+ // ln means the length of an array.
+ // s means a start index.
+ // a is the array-like object the algorithm is applied to.
+ // ret means returnValue.
+ // fn is the main functional argument to the algorithm.
+ // It may not seem like much, but using these shorter names reduces the
+ // size of the minified library by about 15%.
+
+ // Function ui32(Any n) -> Number
+ // Emulates the internal ToUint32 algorithm in the standard. Convert any
+ // object to a non-negative integer. Negative lengths are a sticking
+ // point. The standard implies that you should execute 2^31 operations if
+ // you get a negative length, but that seems like a mistake to me. Most
+ // browsers don't if you pass in a negative length, although some
+ // "correctly" hang for at least some of the algorithms. Regardless, the
+ // only reasonable thing to do with a negative length is to return
+ // immediately.
+ function ui32(n) {
n = isFinite(n) ? Math.floor(n) : 0;
return n >= 0 ? n : 0;
}
// throw a TypeError if not a function. The standard specifies "callable"
- // rather than functions, and some non- function objects are callable but
+ // rather than functions, and some non-function objects are callable but
// are not functions, like RegExp in some browsers. However some still
// expect "real" functions: in Chrome, [].every(/x/) throws "TypeError: /x/
// is not a function", even though RegExps are callable in Chrome.
// Further, we can't check explicitly for callableness without actually
// calling it but can we check for functions with typeof. Since we do want
// to throw the error even if the array is empty and would prefer to throw
- // a meaningful error message rather than letting it crach later when the
+ // a meaningful error message rather than letting it crash later when the
// function invokation fails, we check for functions rather than the more
// abstract concept of "Callable."
//
// TL;DR In practice "is callable" means "is a function".
- function assertCallable(fn) {
- if ( !(typeof fn === 'function') ) {
+ function canCall(fn) {
+ if ( typeof fn !== 'function' ) {
throw new TypeError(fn + " is not a function");
}
}
// common algorithm for reduce() and reduceRight().
function reduce(args, reverse) {
+ var fn = args[0], // reducer function
+ acc, // the accumulator
+ i = 0, // index
+ a = Object(this),
+ ln = ui32(a.length),
+ more, // internal function to test if loop complete
+ next; // internal function to advance to the next item
+
+ // validate arguments
if ( this == null ) throw new TypeError("missing this argument");
- var fn = args[0];
- assertCallable(fn);
-
- var list = Object(this),
- length = ToUint32(list.length);
-
- if ( length === 0 && args.length < 2 ) throw new TypeError("can't reduce empty array without accumulator");
- var acc, // the accumulator
- i = 0;
+ canCall(fn);
+ if ( ln === 0 && args.length < 2 ) throw new TypeError("can't reduce empty array without accumulator");
if ( reverse ) {
- var notDone = function() { return i >= 0; },
- next = function() { return --i; };
- i = length-1;
+ more = function() { return i >= 0 };
+ next = function() { return i-- };
+ i = ln-1;
} else {
- notDone = function() { return i < length; };
- next = function() { return ++i; };
+ more = function() { return i < ln };
+ next = function() { return i++ };
}
// use the accumulator if explicitly passed in...
@@ -59,18 +75,18 @@
} else {
// or seek forward to find the first value to initialize the accumulator.
var found = false;
- while ( !found && notDone() ) {
- found = (i in list);
- if ( found ) acc = list[i];
+ while ( !found && more() ) {
+ found = (i in a);
+ if ( found ) acc = a[i];
next();
}
if ( !found ) throw new TypeError("can't find an element to initialize accumulator");
}
// combine the accumulator with each forward item.
- while ( notDone() ) {
- if ( i in list ) {
- acc = fn.call(undefined, acc, list[i], i, list);
+ while ( more() ) {
+ if ( i in a ) {
+ acc = fn.call(undefined, acc, a[i], i, a);
}
next();
}
@@ -78,26 +94,28 @@
return acc;
}
- var algorithms = {
+ // algorithms defined in the standard but not available in all environments, namely IE.
+ var std = {
// indexOf(Any value, Number start?) -> Number
indexOf: function(value) {
- var list = Object(this);
- var length = ToUint32(list.length);
- if ( length === 0 ) return -1;
+ var a = Object(this),
+ ln = ui32(a.length),
+ s = parseInt(arguments[1]) || 0,
+ i; // index
+ if ( ln === 0 ) return -1;
// normalize the start index
- var start = parseInt(arguments[1]) || 0;
- if ( start > length ) return -1;
- if ( start < 0 ) {
- start = length + start;
- if ( start < 0 ) start = 0;
+ if ( s > ln ) return -1;
+ if ( s < 0 ) {
+ s = ln + s;
+ if ( s < 0 ) s = 0;
}
// search for the element
- for ( var i=start; i<length; ++i ) {
- if ( i in list ) {
- if ( value === list[i] ) return i;
+ for ( i=s; i<ln; i++) {
+ if ( i in a ) {
+ if ( value === a[i] ) return i;
}
}
return -1;
@@ -105,25 +123,26 @@
// lastIndexOf(Any value, Number start?) -> Number
lastIndexOf: function(value) {
- var list = Object(this);
- var length = ToUint32(list.length);
- if ( length === 0 ) return -1;
+ var a = Object(this),
+ ln = ui32(a.length),
+ s = parseInt(arguments[1]),
+ i; // index
+ if ( ln === 0 ) return -1;
// normalize the start index
// note: while superficially similar to indexOf(), the starting
// index logic for lastIndexOf() is completely different...
- var start = parseInt(arguments[1]);
- if ( start !==0 && !start ) start = length; // hmmm...
- if ( start >= 0 ) {
- if ( start >= length ) length-1;
+ if ( s !==0 && !s ) s = ln; // hmmm...
+ if ( s >= 0 ) {
+ if ( s >= ln ) ln-1;
} else {
- start = length + start;
+ s = ln + s;
}
// search for the element
- for ( var i=start; i>=0; --i ) {
- if ( i in list ) {
- if ( value === list[i] ) return i;
+ for ( i=s; i>=0; i--) {
+ if ( i in a ) {
+ if ( value === a[i] ) return i;
}
}
return -1;
@@ -131,12 +150,13 @@
// every( Function predicate(Any value, Number index, Array array) -> Boolean, Any scope? ) -> Boolean
every: function(fn) {
- var list = Object(this),
- length = ToUint32(list.length),
- scope = arguments[1];
- for ( var i=0; i<length; ++i ) {
- if ( i in list ) {
- if ( !fn.call(scope, list[i], i, list) ) return false;
+ var a = Object(this),
+ ln = ui32(a.length),
+ scope = arguments[1],
+ i; // index
+ for ( i=0; i<ln; i++) {
+ if ( i in a ) {
+ if ( !fn.call(scope, a[i], i, a) ) return false;
}
}
return true;
@@ -144,30 +164,31 @@
// some( Function predicate(Any value, Number index, Array array) -> Boolean, Any scope? ) -> Boolean
some: function(fn) {
- assertCallable(fn);
- return !algorithms.every.call(this, function() {
+ canCall(fn);
+ return !std.every.call(this, function() {
return !fn.apply(this, arguments);
}, arguments[1]);
},
// forEach( Function handler(Any value, Number index, Array array), Any scope? ) -> undefined
forEach: function(fn) {
- assertCallable(fn);
- var list = Object(this);
- var length = ToUint32(list.length);
- var scope = arguments[1];
- for ( var i=0; i <length; ++i ) {
- if ( i in list ) {
- fn.call(scope, list[i], i, list);
+ canCall(fn);
+ var a = Object(this),
+ ln = ui32(a.length),
+ scope = arguments[1],
+ i; // inex
+ for ( i=0; i <ln; i++ ) {
+ if ( i in a ) {
+ fn.call(scope, a[i], i, a);
}
}
},
// filter( Function predicate(Any value, Number index, Array array) -> Boolean, Any scope? ) -> Array
filter: function(fn) {
- assertCallable(fn);
+ canCall(fn);
var ret = [];
- algorithms.forEach.call(this, function(value) {
+ std.forEach.call(this, function(value) {
if ( fn.apply(this, arguments) ) ret.push(value);
}, arguments[1]);
return ret;
@@ -175,9 +196,9 @@
// map( Function mapper(Any value, Number index, Array array) -> Any, Any scope? ) -> Array
map: function(fn) {
- assertCallable(fn);
+ canCall(fn);
var ret = [];
- algorithms.forEach.call(this, function(value) {
+ std.forEach.call(this, function(value) {
ret.push( fn.apply(this, arguments) );
}, arguments[1]);
return ret;
@@ -196,10 +217,16 @@
// install missing methods onto Array.prototype
var ap = Array.prototype;
- for ( var key in algorithms ) if ( !(key in ap) ) ap[key] = algorithms[key];
+ for ( var key in std ) if ( !(key in ap) ) ap[key] = std[key];
+
+ // Function ns(Object base, String name) -> Object
+ // makes sure that the key name exists in the base
+ // object, creates it if it does not, and returns it.
+ function ns(b, n) {
+ return n in b ? b[n] : b[n] = {};
+ }
+
+ // expose the standard algorithms in a public namespace, in case they're needed later.
+ ns(ns(window, 'attache'), 'array').standard = std;
- // expose the algorithms in a public namespace, too.
- attache = window.attache || {};
- attache.array = algorithms;
-
})();
View
15 attache.array.min.js
@@ -1,16 +1,3 @@
// The author disclaims copyright to this code.
-(function(){function ToUint32(n){n=isFinite(n)?Math.floor(n):0;return n>=0?n:0;}
-function assertCallable(fn){if(!(typeof fn==='function')){throw new TypeError(fn+" is not a function");}}
-function reduce(args,reverse){if(this==null)throw new TypeError("missing this argument");var fn=args[0];assertCallable(fn);var list=Object(this),length=ToUint32(list.length);if(length===0&&args.length<2)throw new TypeError("can't reduce empty array without accumulator");var acc,i=0;if(reverse){var notDone=function(){return i>=0;},next=function(){return--i;};i=length-1;}else{notDone=function(){return i<length;};next=function(){return++i;};}
-if(args.length>=2){acc=args[1];}else{var found=false;while(!found&&notDone()){found=(i in list);if(found)acc=list[i];next();}
-if(!found)throw new TypeError("can't find an element to initialize accumulator");}
-while(notDone()){if(i in list){acc=fn.call(undefined,acc,list[i],i,list);}
-next();}
-return acc;}
-var algorithms={indexOf:function(value){var list=Object(this);var length=ToUint32(list.length);if(length===0)return-1;var start=parseInt(arguments[1])||0;if(start>length)return-1;if(start<0){start=length+start;if(start<0)start=0;}
-for(var i=start;i<length;++i){if(i in list){if(value===list[i])return i;}}
-return-1;},lastIndexOf:function(value){var list=Object(this);var length=ToUint32(list.length);if(length===0)return-1;var start=parseInt(arguments[1]);if(start!==0&&!start)start=length;if(start>=0){if(start>=length)length-1;}else{start=length+start;}
-for(var i=start;i>=0;--i){if(i in list){if(value===list[i])return i;}}
-return-1;},every:function(fn){var list=Object(this),length=ToUint32(list.length),scope=arguments[1];for(var i=0;i<length;++i){if(i in list){if(!fn.call(scope,list[i],i,list))return false;}}
-return true;},some:function(fn){assertCallable(fn);return!algorithms.every.call(this,function(){return!fn.apply(this,arguments);},arguments[1]);},forEach:function(fn){assertCallable(fn);var list=Object(this);var length=ToUint32(list.length);var scope=arguments[1];for(var i=0;i<length;++i){if(i in list){fn.call(scope,list[i],i,list);}}},filter:function(fn){assertCallable(fn);var ret=[];algorithms.forEach.call(this,function(value){if(fn.apply(this,arguments))ret.push(value);},arguments[1]);return ret;},map:function(fn){assertCallable(fn);var ret=[];algorithms.forEach.call(this,function(value){ret.push(fn.apply(this,arguments));},arguments[1]);return ret;},reduce:function(fn){return reduce.call(this,arguments,false);},reduceRight:function(fn){return reduce.call(this,arguments,true);}};var ap=Array.prototype;for(var key in algorithms)if(!(key in ap))ap[key]=algorithms[key];attache=window.attache||{};attache.array=algorithms;})();
+(function(){"use strict";function ui32(n){n=isFinite(n)?Math.floor(n):0;return n>=0?n:0;}function canCall(fn){if(typeof fn!=='function'){throw new TypeError(fn+" is not a function");}}function reduce(args,reverse){var fn=args[0],acc,i=0,a=Object(this),ln=ui32(a.length),more,next;if(this==null)throw new TypeError("missing this argument");canCall(fn);if(ln===0&&args.length<2)throw new TypeError("can't reduce empty array without accumulator");if(reverse){more=function(){return i>=0};next=function(){return i--};i=ln-1;}else{more=function(){return i<ln};next=function(){return i++};}if(args.length>=2){acc=args[1];}else{var found=false;while(!found&&more()){found=(i in a);if(found)acc=a[i];next();}if(!found)throw new TypeError("can't find an element to initialize accumulator");}while(more()){if(i in a){acc=fn.call(undefined,acc,a[i],i,a);}next();}return acc;}var std={indexOf:function(value){var a=Object(this),ln=ui32(a.length),s=parseInt(arguments[1])||0,i;if(ln===0)return-1;if(s>ln)return-1;if(s<0){s=ln+s;if(s<0)s=0;}for(i=s;i<ln;i++){if(i in a){if(value===a[i])return i;}}return-1;},lastIndexOf:function(value){var a=Object(this),ln=ui32(a.length),s=parseInt(arguments[1]),i;if(ln===0)return-1;if(s!==0&&!s)s=ln;if(s>=0){if(s>=ln)ln-1;}else{s=ln+s;}for(i=s;i>=0;i--){if(i in a){if(value===a[i])return i;}}return-1;},every:function(fn){var a=Object(this),ln=ui32(a.length),scope=arguments[1],i;for(i=0;i<ln;i++){if(i in a){if(!fn.call(scope,a[i],i,a))return false;}}return true;},some:function(fn){canCall(fn);return!std.every.call(this,function(){return!fn.apply(this,arguments);},arguments[1]);},forEach:function(fn){canCall(fn);var a=Object(this),ln=ui32(a.length),scope=arguments[1],i;for(i=0;i<ln;i++){if(i in a){fn.call(scope,a[i],i,a);}}},filter:function(fn){canCall(fn);var ret=[];std.forEach.call(this,function(value){if(fn.apply(this,arguments))ret.push(value);},arguments[1]);return ret;},map:function(fn){canCall(fn);var ret=[];std.forEach.call(this,function(value){ret.push(fn.apply(this,arguments));},arguments[1]);return ret;},reduce:function(fn){return reduce.call(this,arguments,false);},reduceRight:function(fn){return reduce.call(this,arguments,true);}};var ap=Array.prototype;for(var key in std)if(!(key in ap))ap[key]=std[key];function ns(b,n){return n in b?b[n]:b[n]={};}ns(ns(window,'attache'),'array').standard=std;})();
View
7 test/attache.array.test.js
@@ -1,7 +1,7 @@
// forcibly install our algorithms for testing.
-(function(aa, ap) {
- for ( var k in aa ) ap[k] = aa[k];
-})(attache.array, Array.prototype);
+(function(aas, ap) {
+ for ( var k in aas ) ap[k] = aas[k];
+})(attache.array.standard, Array.prototype);
(function() {
module("attache.array");
@@ -247,7 +247,6 @@
// when working on array-like objects, missing keys are safely skipped
var psuedoArray = { 0: 1, 1: 2, 3: 3, 4:4, length: 5 };
equals(Array.prototype[method].call(psuedoArray, sum), 10);
-
}
// non-associative reducers used by both the reduce() and reduceRight() tests,

0 comments on commit aa3544f

Please sign in to comment.
Something went wrong with that request. Please try again.