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

@@ -813,6 +813,18 @@ _.isElement(jQuery('body')[0]);
=> false
_.isArray([1,2,3]);
=> true
</pre>
<p id="isArguments">
<b class="header">isArguments</b><code>_.isArguments(object)</code>
<br />
Returns <i>true</i> if <b>object</b> is an Arguments object.
</p>
<pre>
(function(){ return _.isArguments(arguments); })(1, 2, 3);
=&gt; true
_.isArguments([1,2,3]);
=&gt; false
</pre>
<p id="isFunction">

View File

@@ -29,6 +29,10 @@ $(document).ready(function() {
answer = null;
_.each([1, 2, 3], function(num, index, arr){ if (_.include(arr, num)) answer = true; });
ok(answer, 'can reference the original collection from inside the iterator');
answers = [];
_.each({range : 1, speed : 2, length : 3}, function(v){ answers.push(v); });
ok(answers.join(', '), '1, 2, 3', 'can iterate over objects with numeric length properties');
});
test('collections: map', function() {

View File

@@ -14,7 +14,7 @@ $(document).ready(function() {
var expected = ["all", "any", "bind", "bindAll", "breakLoop", "clone", "compact",
"compose","defer", "delay", "detect", "each", "every", "extend", "filter", "first",
"flatten", "foldl", "foldr", "forEach", "functions", "head", "identity", "include",
"indexOf", "inject", "intersect", "invoke", "isArray", "isDate", "isElement", "isEmpty", "isEqual",
"indexOf", "inject", "intersect", "invoke", "isArguments", "isArray", "isDate", "isElement", "isEmpty", "isEqual",
"isFunction", "isNaN", "isNull", "isNumber", "isRegExp", "isString", "isUndefined", "keys", "last", "lastIndexOf", "map", "max",
"methods", "min", "noConflict", "pluck", "range", "reduce", "reduceRight", "reject", "rest", "select",
"size", "some", "sortBy", "sortedIndex", "tail", "template", "toArray", "uniq",
@@ -71,6 +71,13 @@ $(document).ready(function() {
ok(_.isElement($('html')[0]), 'the html tag is a DOM element');
});
test("objects: isArguments", function() {
var args = (function(){ return arguments; })(1, 2, 3);
ok(_.isArguments(args), 'the arguments object is an arguments object');
ok(!_.isArguments(_.toArray(args)), 'but not when it\'s converted into an array');
ok(!_.isArguments([1,2,3]), 'and not vanilla arrays.');
});
test("objects: isArray", function() {
ok(!_.isArray(arguments), 'the arguments object is not an array');
ok(_.isArray([1, 2, 3]), 'but arrays are');

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);