From 086669fbe07cac3bdafdbe56787e08bb9afadda3 Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Sun, 5 May 2013 23:58:33 -0700 Subject: [PATCH] Ensure `_.forIn` works over objects with longer inheritance chains in IE < 9. Former-commit-id: 226223454e71dd8cb6c38a543f1accd915eef3cb --- build/pre-compile.js | 7 +++++++ lodash.js | 45 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/build/pre-compile.js b/build/pre-compile.js index 4b4d8e3bc..071c8f31c 100644 --- a/build/pre-compile.js +++ b/build/pre-compile.js @@ -11,6 +11,7 @@ 'argsIndex', 'argsLength', 'callback', + 'className', 'collection', 'ctor', 'guard', @@ -18,15 +19,21 @@ 'index', 'isArguments', 'isArray', + 'isProto', 'isString', 'iterable', + 'iterated', 'length', 'keys', 'lodash', + 'nonEnum', + 'nonEnumProps', 'object', + 'objectRef', 'objectTypes', 'ownIndex', 'ownProps', + 'proto', 'result', 'skipProto', 'source', diff --git a/lodash.js b/lodash.js index 20ca1042c..b9f037911 100644 --- a/lodash.js +++ b/lodash.js @@ -217,6 +217,25 @@ ctorByClass[regexpClass] = RegExp; ctorByClass[stringClass] = String; + /** 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 }; + + (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; + } + } + } + }()); + /*--------------------------------------------------------------------------*/ /** @@ -555,13 +574,21 @@ // 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 ctor = iterable.constructor;\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' + + ' <% for (k = 0; k < 7; k++) { %>\n' + " index = '<%= shadowedProps[k] %>';\n" + - ' if (<%' + - " if (shadowedProps[k] == 'constructor') {" + - ' %>!(ctor && ctor.prototype === iterable) && <%' + - ' } %>hasOwnProperty.call(iterable, index)) {\n' + + ' if (!iterated[index] && (iterated[index] = (!(isProto && nonEnum[index]) && hasOwnProperty.call(iterable, index))<%' + + ' if (!useHas) { %> || (!nonEnum[index] && iterable[index] !== objectRef[index])<% }' + + ' %>)) {\n' + ' <%= loop %>\n' + ' }' + ' <% } %>' + @@ -778,14 +805,14 @@ // create the function factory var factory = Function( - 'hasOwnProperty, isArguments, isArray, isString, keys, ' + - 'lodash, objectTypes', + 'hasOwnProperty, isArguments, isArray, isString, keys, lodash, ' + + 'objectRef, objectTypes, nonEnumProps, toString', 'return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}' ); // return the compiled function return factory( - hasOwnProperty, isArguments, isArray, isString, keys, - lodash, objectTypes + hasOwnProperty, isArguments, isArray, isString, keys, lodash, + objectRef, objectTypes, nonEnumProps, toString ); }