Add more _.forIn iteration tests and prep support for Error object iteration.

Former-commit-id: 3676681717d0648c9f96570a4952f7c35e6a9bec
This commit is contained in:
John-David Dalton
2013-05-07 01:38:13 -07:00
parent aad55fc3db
commit e0cf4e644b
3 changed files with 102 additions and 56 deletions

106
lodash.js
View File

@@ -81,9 +81,9 @@
/** Used to assign default `context` object properties */
var contextProps = [
'Array', 'Boolean', 'Date', 'Function', 'Math', 'Number', 'Object', 'RegExp',
'String', '_', 'attachEvent', 'clearTimeout', 'isFinite', 'isNaN', 'parseInt',
'setImmediate', 'setTimeout'
'Array', 'Boolean', 'Date', 'Error', 'Function', 'Math', 'Number', 'Object',
'RegExp', 'String', '_', 'attachEvent', 'clearTimeout', 'isFinite', 'isNaN',
'parseInt', 'setImmediate', 'setTimeout'
];
/** Used to fix the JScript [[DontEnum]] bug */
@@ -100,6 +100,7 @@
arrayClass = '[object Array]',
boolClass = '[object Boolean]',
dateClass = '[object Date]',
errorClass = '[object Error]',
funcClass = '[object Function]',
numberClass = '[object Number]',
objectClass = '[object Object]',
@@ -157,6 +158,7 @@
var Array = context.Array,
Boolean = context.Boolean,
Date = context.Date,
Error = context.Error,
Function = context.Function,
Math = context.Math,
Number = context.Number,
@@ -166,15 +168,17 @@
TypeError = context.TypeError;
/** Used for `Array` and `Object` method references */
var arrayRef = Array(),
objectRef = Object();
var arrayProto = Array.prototype,
errorProto = Error.prototype,
objectProto = Object.prototype,
stringProto = String.prototype;
/** Used to restore the original `_` reference in `noConflict` */
var oldDash = context._;
/** Used to detect if a method is native */
var reNative = RegExp('^' +
String(objectRef.valueOf)
String(objectProto.valueOf)
.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
.replace(/valueOf|for [^\]]+/g, '.+?') + '$'
);
@@ -182,14 +186,14 @@
/** Native method shortcuts */
var ceil = Math.ceil,
clearTimeout = context.clearTimeout,
concat = arrayRef.concat,
concat = arrayProto.concat,
floor = Math.floor,
getPrototypeOf = reNative.test(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf,
hasOwnProperty = objectRef.hasOwnProperty,
push = arrayRef.push,
hasOwnProperty = objectProto.hasOwnProperty,
push = arrayProto.push,
setImmediate = context.setImmediate,
setTimeout = context.setTimeout,
toString = objectRef.toString;
toString = objectProto.toString;
/* Native method shortcuts for methods with the same name as other `lodash` methods */
var nativeBind = reNative.test(nativeBind = toString.bind) && nativeBind,
@@ -201,7 +205,7 @@
nativeMin = Math.min,
nativeParseInt = context.parseInt,
nativeRandom = Math.random,
nativeSlice = arrayRef.slice;
nativeSlice = arrayProto.slice;
/** Detect various environments */
var isIeOpera = reNative.test(context.attachEvent),
@@ -212,6 +216,8 @@
ctorByClass[arrayClass] = Array;
ctorByClass[boolClass] = Boolean;
ctorByClass[dateClass] = Date;
ctorByClass[errorClass] = Error;
ctorByClass[funcClass] = Function;
ctorByClass[objectClass] = Object;
ctorByClass[numberClass] = Number;
ctorByClass[regexpClass] = RegExp;
@@ -219,18 +225,18 @@
/** Used to avoid iterating non-enumerable properties in IE < 9 */
var nonEnumProps = {};
nonEnumProps[arrayClass] = nonEnumProps[dateClass] = nonEnumProps[numberClass] = { 'constructor': 1, 'toLocaleString': 1, 'toString': 1, 'valueOf': 1 };
nonEnumProps[boolClass] = nonEnumProps[stringClass] = { 'constructor': 1, 'toString': 1, 'valueOf': 1 };
nonEnumProps[funcClass] = nonEnumProps[regexpClass] = { 'constructor': 1, 'toString': 1 };
nonEnumProps[objectClass] = { 'constructor': 1 };
nonEnumProps[arrayClass] = nonEnumProps[dateClass] = nonEnumProps[numberClass] = { 'constructor': true, 'toLocaleString': true, 'toString': true, 'valueOf': true };
nonEnumProps[boolClass] = nonEnumProps[stringClass] = { 'constructor': true, 'toString': true, 'valueOf': true };
nonEnumProps[errorClass] = nonEnumProps[funcClass] = nonEnumProps[regexpClass] = { 'constructor': true, 'toString': true };
nonEnumProps[objectClass] = { 'constructor': true };
(function() {
var length = shadowedProps.length;
while (length--) {
var prop = shadowedProps[length];
for (var className in nonEnumProps) {
if (hasOwnProperty.call(nonEnumProps, className) && nonEnumProps[className][prop] !== 1) {
nonEnumProps[className][prop] = 0;
if (hasOwnProperty.call(nonEnumProps, className) && !hasOwnProperty.call(nonEnumProps[className], prop)) {
nonEnumProps[className][prop] = false;
}
}
}
@@ -400,7 +406,7 @@
* @memberOf _.support
* @type Boolean
*/
support.spliceObjects = (arrayRef.splice.call(object, 0, 1), !object[0]);
support.spliceObjects = (arrayProto.splice.call(object, 0, 1), !object[0]);
/**
* Detect lack of support for accessing string characters by index.
@@ -574,24 +580,24 @@
// defaults to non-enumerable, Lo-Dash skips the `constructor`
// property when it infers it's iterating over a `prototype` object.
' <% if (support.nonEnumShadows) { %>\n\n' +
' var iterated = {' +
' <% for (var k = 0; k < 7; k++) { %>\n' +
" '<%= shadowedProps[k] %>': 0<%= (k < 6 ? ',' : '') %>" +
' <% } %>\n' +
' };\n\n' +
' var className = toString.call(iterable),\n' +
' ctor = iterable.constructor,\n' +
' proto = ctor && ctor.prototype,\n' +
' isProto = iterable === proto,\n' +
' nonEnum = nonEnumProps[className];\n\n' +
' if (iterable !== objectProto) {\n' +
" var ctor = iterable.constructor,\n" +
' proto = ctor && ctor.prototype,\n' +
' isProto = iterable === proto,\n' +
' nonEnum = nonEnumProps[objectClass];\n\n' +
' if (isProto) {\n' +
" var className = iterable === stringProto ? stringClass : iterable === errorProto ? errorClass : toString.call(iterable),\n" +
' nonEnum = nonEnumProps[iterable === (ctorByClass[className] && ctorByClass[className].prototype) ? className : objectClass];\n' +
' }\n' +
' <% for (k = 0; k < 7; k++) { %>\n' +
" index = '<%= shadowedProps[k] %>';\n" +
' if (!iterated[index] && (iterated[index] = (!(isProto && nonEnum[index]) && hasOwnProperty.call(iterable, index))<%' +
' if (!useHas) { %> || (!nonEnum[index] && iterable[index] !== objectRef[index])<% }' +
' %>)) {\n' +
' <%= loop %>\n' +
" index = '<%= shadowedProps[k] %>';\n" +
' if ((!(isProto && nonEnum[index]) && hasOwnProperty.call(iterable, index))<%' +
' if (!useHas) { %> || (!nonEnum[index] && iterable[index] !== objectProto[index])<% }' +
' %>) {\n' +
' <%= loop %>\n' +
' }' +
' <% } %>\n' +
' }' +
' <% } %>' +
' <% } %>' +
' <% } %>' +
' <% if (arrays || support.nonEnumArgs) { %>\n}<% } %>\n' +
@@ -805,14 +811,16 @@
// create the function factory
var factory = Function(
'hasOwnProperty, isArguments, isArray, isString, keys, lodash, ' +
'objectRef, objectTypes, nonEnumProps, toString',
'ctorByClass, errorClass, errorProto, hasOwnProperty, isArguments, ' +
'isArray, isString, keys, lodash, objectClass, objectProto, objectTypes, ' +
'nonEnumProps, stringClass, stringProto, toString',
'return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}'
);
// return the compiled function
return factory(
hasOwnProperty, isArguments, isArray, isString, keys, lodash,
objectRef, objectTypes, nonEnumProps, toString
ctorByClass, errorClass, errorProto, hasOwnProperty, isArguments,
isArray, isString, keys, lodash, objectClass, objectProto, objectTypes,
nonEnumProps, stringClass, stringProto, toString
);
}
@@ -2166,7 +2174,7 @@
if (isFunc) {
callback = lodash.createCallback(callback, thisArg);
} else {
var props = concat.apply(arrayRef, nativeSlice.call(arguments, 1));
var props = concat.apply(arrayProto, nativeSlice.call(arguments, 1));
}
forIn(object, function(value, key, object) {
if (isFunc
@@ -2235,7 +2243,7 @@
var result = {};
if (typeof callback != 'function') {
var index = -1,
props = concat.apply(arrayRef, nativeSlice.call(arguments, 1)),
props = concat.apply(arrayProto, nativeSlice.call(arguments, 1)),
length = isObject(object) ? props.length : 0;
while (++index < length) {
@@ -2305,7 +2313,7 @@
*/
function at(collection) {
var index = -1,
props = concat.apply(arrayRef, nativeSlice.call(arguments, 1)),
props = concat.apply(arrayProto, nativeSlice.call(arguments, 1)),
length = props.length,
result = Array(length);
@@ -3347,7 +3355,7 @@
function difference(array) {
var index = -1,
length = array ? array.length : 0,
flattened = concat.apply(arrayRef, nativeSlice.call(arguments, 1)),
flattened = concat.apply(arrayProto, nativeSlice.call(arguments, 1)),
contains = cachedContains(flattened),
result = [];
@@ -4024,9 +4032,9 @@
*/
function union(array) {
if (!isArray(array)) {
arguments[0] = array ? nativeSlice.call(array) : arrayRef;
arguments[0] = array ? nativeSlice.call(array) : arrayProto;
}
return uniq(concat.apply(arrayRef, arguments));
return uniq(concat.apply(arrayProto, arguments));
}
/**
@@ -4303,7 +4311,7 @@
* // => alerts 'clicked docs', when the button is clicked
*/
function bindAll(object) {
var funcs = arguments.length > 1 ? concat.apply(arrayRef, nativeSlice.call(arguments, 1)) : functions(object),
var funcs = arguments.length > 1 ? concat.apply(arrayProto, nativeSlice.call(arguments, 1)) : functions(object),
index = -1,
length = funcs.length;
@@ -5482,7 +5490,7 @@
// add `Array` functions that return unwrapped values
each(['join', 'pop', 'shift'], function(methodName) {
var func = arrayRef[methodName];
var func = arrayProto[methodName];
lodash.prototype[methodName] = function() {
return func.apply(this.__wrapped__, arguments);
};
@@ -5490,7 +5498,7 @@
// add `Array` functions that return the wrapped value
each(['push', 'reverse', 'sort', 'unshift'], function(methodName) {
var func = arrayRef[methodName];
var func = arrayProto[methodName];
lodash.prototype[methodName] = function() {
func.apply(this.__wrapped__, arguments);
return this;
@@ -5499,7 +5507,7 @@
// add `Array` functions that return new wrapped values
each(['concat', 'slice', 'splice'], function(methodName) {
var func = arrayRef[methodName];
var func = arrayProto[methodName];
lodash.prototype[methodName] = function() {
return new lodashWrapper(func.apply(this.__wrapped__, arguments));
};
@@ -5509,7 +5517,7 @@
// in Firefox < 10 and IE < 9
if (!support.spliceObjects) {
each(['pop', 'shift', 'splice'], function(methodName) {
var func = arrayRef[methodName],
var func = arrayProto[methodName],
isSplice = methodName == 'splice';
lodash.prototype[methodName] = function() {