Optimize object iteration using Object.keys where faster than for-in loops.

Former-commit-id: 8826f75cf6eaf4233758684e3aae2ccdc3c9f262
This commit is contained in:
John-David Dalton
2012-07-08 01:55:33 -04:00
parent 6d217fc097
commit 04d4353c0f
2 changed files with 43 additions and 13 deletions

View File

@@ -23,11 +23,15 @@
'iteratorBind', 'iteratorBind',
'length', 'length',
'methodName', 'methodName',
'nativeKeys',
'noaccum', 'noaccum',
'object', 'object',
'objectTypes', 'objectTypes',
'prop', 'prop',
'propIndex',
'props',
'property', 'property',
'propertyIsEnumerable',
'result', 'result',
'skipProto', 'skipProto',
'slice', 'slice',
@@ -59,7 +63,8 @@
'objectBranch', 'objectBranch',
'shadowed', 'shadowed',
'top', 'top',
'useHas' 'useHas',
'useNativeKeys'
]; ];
/** Used to minify variables and string values to a single character */ /** Used to minify variables and string values to a single character */
@@ -74,6 +79,7 @@
'all', 'all',
'amd', 'amd',
'any', 'any',
'attachEvent',
'bind', 'bind',
'bindAll', 'bindAll',
'chain', 'chain',

View File

@@ -131,6 +131,9 @@
/* Detect if `Function#bind` exists and is inferred to be fast (i.e. all but V8) */ /* Detect if `Function#bind` exists and is inferred to be fast (i.e. all but V8) */
var useNativeBind = nativeBind && /\n|Opera/.test(nativeBind + toString.call(window.opera)); var useNativeBind = nativeBind && /\n|Opera/.test(nativeBind + toString.call(window.opera));
/* Detect if `Object.keys` exists and is inferred to be fast (i.e. V8, Opera, IE) */
var useNativeKeys = nativeKeys && /^.+$|true/.test(nativeKeys + !!window.attachEvent);
/** Detect if sourceURL syntax is usable without erroring */ /** Detect if sourceURL syntax is usable without erroring */
try { try {
// Adobe's and Narwhal's JS engines will error // Adobe's and Narwhal's JS engines will error
@@ -279,17 +282,34 @@
// the following branch is for iterating an object's own/inherited properties // the following branch is for iterating an object's own/inherited properties
'if (objectBranch) {' + 'if (objectBranch) {' +
' if (arrayBranch) { %>else {\n<% }' + ' if (arrayBranch) { %>else {\n<% }' +
' if (!hasDontEnumBug) { %> var skipProto = typeof <%= iteratedObject %> == \'function\';\n<% } %>' + ' if (!hasDontEnumBug) { %>' +
' <%= objectBranch.beforeLoop %>;\n' + ' var skipProto = typeof <%= iteratedObject %> == \'function\' && \n' +
' propertyIsEnumerable.call(<%= iteratedObject %>, \'prototype\');\n<%' +
' } %>' +
' <%= objectBranch.beforeLoop %>;\n<%' +
// iterate own properties using `Object.keys` if it's fast
' if (useNativeKeys && useHas) { %>' +
' var props = nativeKeys(<%= iteratedObject %>),\n' +
' propIndex = -1,\n' +
' length = props.length;\n' +
' while (++propIndex < length) {\n' +
' index = props[propIndex];\n' +
' if (!(skipProto && index == \'prototype\')) {\n' +
' <%= objectBranch.inLoop %>\n' +
' }\n' +
' }\n' +
// else using a for-in loop
' <% } else { %>' +
' for (<%= objectBranch.loopExp %>) {' + ' for (<%= objectBranch.loopExp %>) {' +
' \n<%' + ' \n<%' +
' if (hasDontEnumBug) {' + ' if (hasDontEnumBug) {' +
' if (useHas) { %> if (<%= hasExp %>) {\n <% } %>' + ' if (useHas) { %> if (<%= hasExp %>) {\n <% } %>' +
' <%= objectBranch.inLoop %>;<%' + ' <%= objectBranch.inLoop %>;<%' +
' if (useHas) { %>\n }<% }' + ' if (useHas) { %>\n }<% }' +
' }' + ' }' +
' else {' + ' else { %>' +
' %>' +
// Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1 // Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1
// (if the prototype or a property on the prototype has been set) // (if the prototype or a property on the prototype has been set)
@@ -300,8 +320,9 @@
' if (!(skipProto && index == \'prototype\')<% if (useHas) { %> && <%= hasExp %><% } %>) {\n' + ' if (!(skipProto && index == \'prototype\')<% if (useHas) { %> && <%= hasExp %><% } %>) {\n' +
' <%= objectBranch.inLoop %>\n' + ' <%= objectBranch.inLoop %>\n' +
' }' + ' }' +
' <% } %>\n' + ' <% } %>\n' +
' }' + ' }' +
' <% } %>' +
// Because IE < 9 can't set the `[[Enumerable]]` attribute of an // Because IE < 9 can't set the `[[Enumerable]]` attribute of an
// existing property and the `constructor` property of a prototype // existing property and the `constructor` property of a prototype
@@ -309,7 +330,7 @@
// property when it infers it's iterating over a `prototype` object. // property when it infers it's iterating over a `prototype` object.
' <% if (hasDontEnumBug) { %>\n' + ' <% if (hasDontEnumBug) { %>\n' +
' var ctor = <%= iteratedObject %>.constructor;\n' + ' var ctor = <%= iteratedObject %>.constructor;\n' +
' <% for (var k = 0; k < 7; k++) { %>\n' + ' <% for (var k = 0; k < 7; k++) { %>\n' +
' index = \'<%= shadowed[k] %>\';\n' + ' index = \'<%= shadowed[k] %>\';\n' +
' if (<%' + ' if (<%' +
' if (shadowed[k] == \'constructor\') {' + ' if (shadowed[k] == \'constructor\') {' +
@@ -479,6 +500,7 @@
data.firstArg = firstArg; data.firstArg = firstArg;
data.hasDontEnumBug = hasDontEnumBug; data.hasDontEnumBug = hasDontEnumBug;
data.useNativeKeys = useNativeKeys;
data.hasExp = 'hasOwnProperty.call(' + iteratedObject + ', index)'; data.hasExp = 'hasOwnProperty.call(' + iteratedObject + ', index)';
data.iteratedObject = iteratedObject; data.iteratedObject = iteratedObject;
data.shadowed = shadowed; data.shadowed = shadowed;
@@ -496,13 +518,15 @@
// create the function factory // create the function factory
var factory = Function( var factory = Function(
'arrayClass, compareAscending, funcClass, hasOwnProperty, identity, ' + 'arrayClass, compareAscending, funcClass, hasOwnProperty, identity, ' +
'iteratorBind, objectTypes, slice, stringClass, toString', 'iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, ' +
'slice, stringClass, toString',
'"use strict"; return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}' '"use strict"; return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}'
); );
// return the compiled function // return the compiled function
return factory( return factory(
arrayClass, compareAscending, funcClass, hasOwnProperty, identity, arrayClass, compareAscending, funcClass, hasOwnProperty, identity,
iteratorBind, objectTypes, slice, stringClass, toString iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, slice,
stringClass, toString
); );
} }