8000 Optimize object iteration using `Object.keys` where faster than for-i… · lodash/lodash@04d4353 · GitHub
[go: up one dir, main page]

Skip to content

Commit 04d4353

Browse files
committed
Optimize object iteration using Object.keys where faster than for-in loops.
Former-commit-id: 8826f75cf6eaf4233758684e3aae2ccdc3c9f262
1 parent 6d217fc commit 04d4353

File tree

2 files changed

+43
-13
lines changed

2 files changed

+43
-13
lines changed

build/pre-compile.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,15 @@
2323
'iteratorBind',
2424
'length',
2525
'methodName',
26+
'nativeKeys',
2627
'noaccum',
2728
'object',
2829
'objectTypes',
2930
'prop',
31+
'propIndex',
32+
'props',
3033
'property',
34+
'propertyIsEnumerable',
3135
'result',
3236
'skipProto',
3337
'slice',
@@ -59,7 +63,8 @@
5963
'objectBranch',
6064
'shadowed',
6165
'top',
62-
'useHas'
66+
'useHas',
67+
'useNativeKeys'
6368
];
6469

6570
/** Used to minify variables and string values to a single character */
@@ -74,6 +79,7 @@
7479
'all',
7580
'amd',
7681
'any',
82+
'attachEvent',
7783
'bind',
7884
'bindAll',
7985
'chain',

lodash.js

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@
131131
/* Detect if `Function#bind` exists and is inferred to be fast (i.e. all but V8) */
132132
var useNativeBind = nativeBind && /\n|Opera/.test(nativeBind + toString.call(window.opera));
133133

134+
/* Detect if `Object.keys` exists and is inferred to be fast (i.e. V8, Opera, IE) */
135+
var useNativeKeys = nativeKeys && /^.+$|true/.test(nativeKeys + !!window.attachEvent);
136+
134137
/** Detect if sourceURL syntax is usable without erroring */
135138
try {
136139
// Adobe's and Narwhal's JS engines will error
@@ -279,17 +282,34 @@
279282
// the following branch is for iterating an object's own/inherited properties
280283
'if (objectBranch) {' +
281284
' if (arrayBranch) { %>else {\n<% }' +
282-
' if (!hasDontEnumBug) { %> var skipProto = typeof <%= iteratedObject %> == \'function\';\n<% } %>' +
283-
' <%= objectBranch.beforeLoop %>;\n' +
285+
' if (!hasDontEnumBug) { %>' +
286+
' var skipProto = typeof <%= iteratedObject %> == \'function\' && \n' +
287+
' propertyIsEnumerable.call(<%= iteratedObject %>, \'prototype\');\n<%' +
288+
' } %>' +
289+
' <%= objectBranch.beforeLoop %>;\n<%' +
290+
291+
// iterate own properties using `Object.keys` if it's fast
292+
' if (useNativeKeys && useHas) { %>' +
293+
' var props = nativeKeys(<%= iteratedObject %>),\n' +
294+
' propIndex = -1,\n' +
295+
' length = props.length;\n' +
296+
' while (++propIndex < length) {\n' +
297+
' index = props[propIndex];\n' +
298+
' if (!(skipProto && index == \'prototype\')) {\n' +
299+
' <%= objectBranch.inLoop %>\n' +
300+
' }\n' +
301+
' }\n' +
302+
303+
// else using a for-in loop
304+
' <% } else { %>' +
284305
' for (<%= objectBranch.loopExp %>) {' +
285306
' \n<%' +
286-
' if (hasDontEnumBug) {' +
287-
' if (useHas) { %> if (<%= hasExp %>) {\n <% } %>' +
307+
' if (hasDontEnumBug) {' +
308+
' if (useHas) { %> if (<%= hasExp %>) {\n <% } %>' +
288309
' <%= objectBranch.inLoop %>;<%' +
289-
' if (useHas) { %>\n }<% }' +
290-
' }' +
291-
' else {' +
292-
' %>' +
310+
' if (useHas) { %>\n }<% }' +
311+
' }' +
312+
' else { %>' +
293313

294314
// Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1
295315
// (if the prototype or a property on the prototype has been set)
@@ -300,16 +320,17 @@
300320
' if (!(skipProto && index == \'prototype\')<% if (useHas) { %> && <%= hasExp %><% } %>) {\n' +
301321
' <%= objectBranch.inLoop %>\n' +
302322
' }' +
303-
' <% } %>\n' +
323+
' <% } %>\n' +
304324
' }' +
325+
' <% } %>' +
305326

306327
// Because IE < 9 can't set the `[[Enumerable]]` attribute of an
307328
// existing property and the `constructor` property of a prototype
308329
// defaults to non-enumerable, Lo-Dash skips the `constructor`
309330
// property when it infers it's iterating over a `prototype` object.
310331
' <% if (hasDontEnumBug) { %>\n' +
311332
' var ctor = <%= iteratedObject %>.constructor;\n' +
312-
' <% for (var k = 0; k < 7; k++) { %>\n' +
333+
' <% for (var k = 0; k < 7; k++) { %>\n' +
313334
' index = \'<%= shadowed[k] %>\';\n' +
314335
' if (<%' +
315336
' if (shadowed[k] == \'constructor\') {' +
@@ -479,6 +500,7 @@
479500

480501
data.firstArg = firstArg;
481502
data.hasDontEnumBug = hasDontEnumBug;
503+
data.useNativeKeys = useNativeKeys;
482504
data.hasExp = 'hasOwnProperty.call(' + iteratedObject + ', index)';
483505
data.iteratedObject = iteratedObject;
484506
data.shadowed = shadowed;
@@ -496,13 +518,15 @@
496518
// create the function factory
497519
var factory = Function(
498520
'arrayClass, compareAscending, funcClass, hasOwnProperty, identity, ' +
499-
'iteratorBind, objectTypes, slice, stringClass, toString',
521+
'iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, ' +
522+
'slice, stringClass, toString',
500523
'"use strict"; return function(' + args + ') {\n' + iteratorTemplate(data) 7068 + '\n}'
501524
);
502525
// return the compiled function
503526
return factory(
504527
arrayClass, compareAscending, funcClass, hasOwnProperty, identity,
505-
iteratorBind, objectTypes, slice, stringClass, toString
528+
iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, slice,
529+
stringClass, toString
506530
);
507531
}
508532

0 commit comments

Comments
 (0)
0