adding an isArguments checker and enabling iteration (using each) over JS object hashes that have numeric length properties

This commit is contained in:
Jeremy Ashkenas
2009-12-09 13:41:19 -05:00
parent 225d795836
commit 5c314d206e
4 changed files with 48 additions and 15 deletions

View File

@@ -30,8 +30,12 @@
// Export the Underscore object for CommonJS.
if (typeof exports !== 'undefined') exports._ = _;
// Create quick reference variables for speed access to Object.prototype.
var toString = Object.prototype.toString, hasOwnProperty = Object.prototype.hasOwnProperty;
// Create quick reference variables for speed access to core prototypes.
var slice = Array.prototype.slice,
unshift = Array.prototype.unshift,
toString = Object.prototype.toString,
hasOwnProperty = Object.prototype.hasOwnProperty,
propertyIsEnumerable = Object.prototype.propertyIsEnumerable;
// Current version.
_.VERSION = '0.5.0';
@@ -45,7 +49,7 @@
try {
if (obj.forEach) {
obj.forEach(iterator, context);
} else if (_.isNumber(obj.length)) {
} else if (_.isArray(obj) || _.isArguments(obj)) {
for (var i=0, l=obj.length; i<l; i++) iterator.call(context, obj[i], i, obj);
} else {
var keys = _.keys(obj), l = keys.length;
@@ -218,9 +222,10 @@
// Convert anything iterable into a real, live array.
_.toArray = function(iterable) {
if (!iterable) return [];
if (iterable.toArray) return iterable.toArray();
if (_.isArray(iterable)) return iterable;
if (!iterable) return [];
if (iterable.toArray) return iterable.toArray();
if (_.isArray(iterable)) return iterable;
if (_.isArguments(iterable)) return slice.call(iterable);
return _.map(iterable, function(val){ return val; });
};
@@ -235,7 +240,7 @@
// values in the array. Aliased as "head". The "guard" check allows it to work
// with _.map.
_.first = function(array, n, guard) {
return n && !guard ? Array.prototype.slice.call(array, 0, n) : array[0];
return n && !guard ? slice.call(array, 0, n) : array[0];
};
// Returns everything but the first entry of the array. Aliased as "tail".
@@ -243,7 +248,7 @@
// the rest of the values in the array from that index onward. The "guard"
//check allows it to work with _.map.
_.rest = function(array, index, guard) {
return Array.prototype.slice.call(array, _.isUndefined(index) || guard ? 1 : index);
return slice.call(array, _.isUndefined(index) || guard ? 1 : index);
};
// Get the last element of an array.
@@ -406,6 +411,11 @@
return _.map(obj, _.identity);
};
// Return a sorted list of the function names available in Underscore.
_.functions = function(obj) {
return _.select(_.keys(obj), function(key){ return _.isFunction(obj[key]); }).sort();
};
// Extend a given object with all of the properties in a source object.
_.extend = function(destination, source) {
for (var property in source) destination[property] = source[property];
@@ -464,6 +474,11 @@
return !!(obj && obj.nodeType == 1);
};
// Is a given variable an arguments object?
_.isArguments = function(obj) {
return obj && _.isNumber(obj.length) && !_.isArray(obj) && !propertyIsEnumerable.call(obj, 'length');
};
// Is the given value NaN -- this one is interesting. NaN != NaN, and
// isNaN(undefined) == true, so we make sure it's a number first.
_.isNaN = function(obj) {
@@ -522,11 +537,6 @@
return prefix ? prefix + id : id;
};
// Return a sorted list of the function names available in Underscore.
_.functions = function(obj) {
return _.select(_.keys(obj), function(key){ return _.isFunction(obj[key]); }).sort();
};
// JavaScript templating a-la ERB, pilfered from John Resig's
// "Secrets of the JavaScript Ninja", page 83.
_.template = function(str, data) {
@@ -566,7 +576,7 @@
// Add all of the Underscore functions to the wrapper object.
_.each(_.functions(_), function(name) {
var method = _[name], unshift = Array.prototype.unshift;
var method = _[name];
wrapper.prototype[name] = function() {
unshift.call(arguments, this._wrapped);
return result(method.apply(_, arguments), this._chain);