Much better strategy for calling native Array methods.

Compare an object's function with === to the native
version before calling it.  That is, don't just assume that
because an object has a 'filter' property thats a function,
that we should call that in _.filter().

Expose the native methods found as _.native

On the way towards being able to turn off native implementations,
though there are tradeoffs...
This commit is contained in:
Mike Frawley
2010-02-22 15:50:01 -06:00
parent b774bf7ca9
commit bb250e7205

View File

@@ -37,6 +37,25 @@
hasOwnProperty = Object.prototype.hasOwnProperty,
propertyIsEnumerable = Object.prototype.propertyIsEnumerable;
// All native implementations we hope to use are declared here,
// mostly Array.prototype methods.
//
// 'native' is on the long list of reserved words.
// Ok to use as a property name, though jsLint doesn't agree.
var Native = _['native'] = {
forEach: Array.prototype.forEach,
map: Array.prototype.map,
reduce: Array.prototype.reduce,
reduceRight: Array.prototype.reduceRight,
filter: Array.prototype.filter,
every: Array.prototype.every,
some: Array.prototype.some,
indexOf: Array.prototype.indexOf,
lastIndexOf: Array.prototype.lastIndexOf,
isArray: Array.isArray,
keys: Object.keys
};
// Current version.
_.VERSION = '0.5.8';
@@ -49,7 +68,7 @@
_.forEach = function(obj, iterator, context) {
var index = 0;
try {
if (obj.forEach) {
if (obj.forEach === Native.forEach) {
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);
@@ -66,7 +85,7 @@
// Return the results of applying the iterator to each element.
// Delegates to JavaScript 1.6's native map if available.
_.map = function(obj, iterator, context) {
if (obj && _.isFunction(obj.map)) return obj.map(iterator, context);
if (obj.map === Native.map) return obj.map(iterator, context);
var results = [];
each(obj, function(value, index, list) {
results.push(iterator.call(context, value, index, list));
@@ -78,7 +97,7 @@
// inject, or foldl.
// Delegates to JavaScript 1.8's native reduce if available.
_.reduce = function(obj, memo, iterator, context) {
if (obj && _.isFunction(obj.reduce)) return obj.reduce(_.bind(iterator, context), memo);
if (obj.reduce === Native.reduce) return obj.reduce(_.bind(iterator, context), memo);
each(obj, function(value, index, list) {
memo = iterator.call(context, memo, value, index, list);
});
@@ -88,7 +107,7 @@
// The right-associative version of reduce, also known as foldr. Uses
// Delegates to JavaScript 1.8's native reduceRight if available.
_.reduceRight = function(obj, memo, iterator, context) {
if (obj && _.isFunction(obj.reduceRight)) return obj.reduceRight(_.bind(iterator, context), memo);
if (obj.reduceRight === Native.reduceRight) return obj.reduceRight(_.bind(iterator, context), memo);
var reversed = _.clone(_.toArray(obj)).reverse();
return reduce(reversed, memo, iterator, context);
};
@@ -108,7 +127,7 @@
// Return all the elements that pass a truth test.
// Delegates to JavaScript 1.6's native filter if available.
_.filter = function(obj, iterator, context) {
if (obj && _.isFunction(obj.filter)) return obj.filter(iterator, context);
if (obj.filter === Native.filter) return obj.filter(iterator, context);
var results = [];
each(obj, function(value, index, list) {
iterator.call(context, value, index, list) && results.push(value);
@@ -129,7 +148,7 @@
// Delegates to JavaScript 1.6's native every if available.
_.every = function(obj, iterator, context) {
iterator = iterator || _.identity;
if (obj && _.isFunction(obj.every)) return obj.every(iterator, context);
if (obj.every === Native.every) return obj.every(iterator, context);
var result = true;
each(obj, function(value, index, list) {
if (!(result = result && iterator.call(context, value, index, list))) _.breakLoop();
@@ -141,7 +160,7 @@
// Delegates to JavaScript 1.6's native some if available.
_.some = function(obj, iterator, context) {
iterator = iterator || _.identity;
if (obj && _.isFunction(obj.some)) return obj.some(iterator, context);
if (obj.some === Native.some) return obj.some(iterator, context);
var result = false;
each(obj, function(value, index, list) {
if (result = iterator.call(context, value, index, list)) _.breakLoop();
@@ -326,7 +345,7 @@
// item in an array, or -1 if the item is not included in the array.
// Delegates to JavaScript 1.8's native indexOf if available.
_.indexOf = function(array, item) {
if (array.indexOf) return array.indexOf(item);
if (array.indexOf === Native.indexOf) return array.indexOf(item);
for (var i=0, l=array.length; i<l; i++) if (array[i] === item) return i;
return -1;
};
@@ -334,7 +353,7 @@
// Delegates to JavaScript 1.6's native lastIndexOf if available.
_.lastIndexOf = function(array, item) {
if (array.lastIndexOf) return array.lastIndexOf(item);
if (array.lastIndexOf === Native.lastIndexOf) return array.lastIndexOf(item);
var i = array.length;
while (i--) if (array[i] === item) return i;
return -1;
@@ -416,7 +435,7 @@
// Retrieve the names of an object's properties.
// Delegates to ECMA5's native Object.keys
_.keys = Object.keys || function(obj) {
_.keys = Native.keys || function(obj) {
if (_.isArray(obj)) return _.range(0, obj.length);
var keys = [];
for (var key in obj) if (hasOwnProperty.call(obj, key)) keys.push(key);
@@ -515,7 +534,7 @@
// Is a given array or object empty?
_.isEmpty = function(obj) {
if (_.isArray(obj)) return obj.length===0;
if (_.isArray(obj)) return obj.length === 0;
for (var k in obj) return false;
return true;
};
@@ -527,7 +546,7 @@
// Is a given value an array?
// Delegates to ECMA5's native Array.isArray
_.isArray = Array.isArray || function(obj) {
_.isArray = Native.isArray || function(obj) {
return !!(obj && obj.concat && obj.unshift);
};