Issue #70 -- implementing each, find, all, any, etc. without the use of a try/catch/throw. Minor speedup + avoids destroying the stack trace.

This commit is contained in:
Jeremy Ashkenas
2010-12-01 11:08:34 -05:00
parent 6b8bb0cacd
commit 2d06e1d526
3 changed files with 23 additions and 41 deletions

View File

@@ -17,8 +17,8 @@
// Save the previous value of the `_` variable.
var previousUnderscore = root._;
// Establish the object that gets thrown to break out of a loop iteration.
var breaker = typeof StopIteration !== 'undefined' ? StopIteration : '__break__';
// Establish the object that gets returned to break out of a loop iteration.
var breaker = {};
// Save bytes in the minified (but not gzipped) version:
var ArrayProto = Array.prototype, ObjProto = Object.prototype;
@@ -67,20 +67,20 @@
// Handles objects implementing `forEach`, arrays, and raw objects.
// Delegates to **ECMAScript 5**'s native `forEach` if available.
var each = _.each = _.forEach = function(obj, iterator, context) {
try {
if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context);
} else if (_.isNumber(obj.length)) {
for (var i = 0, l = obj.length; i < l; i++) iterator.call(context, obj[i], i, obj);
} else {
for (var key in obj) {
if (hasOwnProperty.call(obj, key)) iterator.call(context, obj[key], key, obj);
var value;
if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context);
} else if (_.isNumber(obj.length)) {
for (var i = 0, l = obj.length; i < l; i++) {
if (iterator.call(context, obj[i], i, obj) === breaker) return;
}
} else {
for (var key in obj) {
if (hasOwnProperty.call(obj, key)) {
if (iterator.call(context, obj[key], key, obj) === breaker) return;
}
}
} catch(e) {
if (e != breaker) throw e;
}
return obj;
};
// Return the results of applying the iterator to each element.
@@ -126,10 +126,10 @@
// Return the first value which passes a truth test. Aliased as `detect`.
_.find = _.detect = function(obj, iterator, context) {
var result;
each(obj, function(value, index, list) {
any(obj, function(value, index, list) {
if (iterator.call(context, value, index, list)) {
result = value;
_.breakLoop();
return true;
}
});
return result;
@@ -164,7 +164,7 @@
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
var result = true;
each(obj, function(value, index, list) {
if (!(result = result && iterator.call(context, value, index, list))) _.breakLoop();
if (!(result = result && iterator.call(context, value, index, list))) return breaker;
});
return result;
};
@@ -172,12 +172,12 @@
// Determine if at least one element in the object matches a truth test.
// Delegates to **ECMAScript 5**'s native `some` if available.
// Aliased as `any`.
_.some = _.any = function(obj, iterator, context) {
var any = _.some = _.any = function(obj, iterator, context) {
iterator = iterator || _.identity;
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
var result = false;
each(obj, function(value, index, list) {
if (result = iterator.call(context, value, index, list)) _.breakLoop();
if (result = iterator.call(context, value, index, list)) return breaker;
});
return result;
};
@@ -187,8 +187,8 @@
_.include = _.contains = function(obj, target) {
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
var found = false;
each(obj, function(value) {
if (found = value === target) _.breakLoop();
any(obj, function(value) {
if (found = value === target) return true;
});
return found;
};
@@ -642,11 +642,6 @@
for (var i = 0; i < n; i++) iterator.call(context, i);
};
// Break out of the middle of an iteration.
_.breakLoop = function() {
throw breaker;
};
// Add your own custom functions to the Underscore object, ensuring that
// they're correctly added to the OOP wrapper as well.
_.mixin = function(obj) {