From b2c00ce63e400dc411dfec889b8f9b9ced90f16c Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sat, 25 Feb 2017 22:06:31 -0800 Subject: [PATCH 01/11] benchmark: benchmark comparing forEach with for --- benchmark/es/foreach-bench.js | 82 +++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 benchmark/es/foreach-bench.js diff --git a/benchmark/es/foreach-bench.js b/benchmark/es/foreach-bench.js new file mode 100644 index 00000000000000..fea5318bc6669b --- /dev/null +++ b/benchmark/es/foreach-bench.js @@ -0,0 +1,82 @@ +'use strict'; + +const common = require('../common.js'); + +const bench = common.createBenchmark(main, { + method: ['for', 'for-of', 'for-in', 'forEach'], + count: [5, 10, 20, 100], + millions: [5] +}); + +function useFor(n, items, count) { + var i, j; + bench.start(); + for (i = 0; i < n; i++) { + for (j = 0; j < count; j++) { + /* eslint-disable no-unused-vars */ + var item = items[j]; + /* esline-enable no-unused-vars */ + } + } + bench.end(n / 1e6); +} + +function useForOf(n, items) { + var i, item; + bench.start(); + for (i = 0; i < n; i++) { + for (item of items) {} + } + bench.end(n / 1e6); +} + +function useForIn(n, items) { + var i, j, item; + bench.start(); + for (i = 0; i < n; i++) { + for (j in items) { + /* eslint-disable no-unused-vars */ + item = items[j]; + /* esline-enable no-unused-vars */ + } + } + bench.end(n / 1e6); +} + +function useForEach(n, items) { + var i; + bench.start(); + for (i = 0; i < n; i++) { + items.forEach((item) => {}); + } + bench.end(n / 1e6); +} + +function main(conf) { + const n = +conf.millions * 1e6; + const count = +conf.count; + + const items = new Array(count); + var i; + var fn; + for (i = 0; i < count; i++) + items[i] = i; + + switch (conf.method) { + case 'for': + fn = useFor; + break; + case 'for-of': + fn = useForOf; + break; + case 'for-in': + fn = useForIn; + break; + case 'forEach': + fn = useForEach; + break; + default: + throw new Error('Unexpected method'); + } + fn(n, items, count); +} From 4d0d40082df4a2a4880dc319dfe5434a5c99cdf9 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sat, 25 Feb 2017 22:16:57 -0800 Subject: [PATCH 02/11] stream: avoid using forEach --- lib/_stream_readable.js | 9 +++++---- lib/_stream_wrap.js | 5 ++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/_stream_readable.js b/lib/_stream_readable.js index 1f7bdcc2e7c465..5d0e8aa243e9d6 100644 --- a/lib/_stream_readable.js +++ b/lib/_stream_readable.js @@ -34,6 +34,8 @@ var StringDecoder; util.inherits(Readable, Stream); +const kProxyEvents = ['error', 'close', 'destroy', 'pause', 'resume']; + function prependListener(emitter, event, fn) { // Sadly this is not cacheable as some libraries bundle their own // event emitter implementation with them. @@ -828,10 +830,9 @@ Readable.prototype.wrap = function(stream) { } // proxy certain important events. - const events = ['error', 'close', 'destroy', 'pause', 'resume']; - events.forEach(function(ev) { - stream.on(ev, self.emit.bind(self, ev)); - }); + for (var n = 0; n < kProxyEvents.length; n++) { + stream.on(kProxyEvents[n], self.emit.bind(self, kProxyEvents[n])); + } // when we try to consume some more bytes, simply unpause the // underlying stream. diff --git a/lib/_stream_wrap.js b/lib/_stream_wrap.js index fbc32965980e96..2ddf15069e5361 100644 --- a/lib/_stream_wrap.js +++ b/lib/_stream_wrap.js @@ -118,9 +118,8 @@ StreamWrap.prototype.doWrite = function doWrite(req, bufs) { const item = self._enqueue('write', req); self.stream.cork(); - bufs.forEach(function(buf) { - self.stream.write(buf, done); - }); + for (var n = 0; n < bufs.length; n++) + self.stream.write(bufs[n], done); self.stream.uncork(); function done(err) { From a2d8baad154f214af146a2f5827d357e3336e774 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sat, 25 Feb 2017 22:41:45 -0800 Subject: [PATCH 03/11] tls: avoid using forEach --- lib/_tls_wrap.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index 64c82b6957efb4..4c9294fb4e69b1 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -321,12 +321,16 @@ var proxiedMethods = [ ]; // Proxy HandleWrap, PipeWrap and TCPWrap methods -proxiedMethods.forEach(function(name) { - tls_wrap.TLSWrap.prototype[name] = function methodProxy(...args) { +function makeMethodProxy(name) { + return function methodProxy(...args) { if (this._parent[name]) return this._parent[name].apply(this._parent, args); }; -}); +} +for (var n = 0; n < proxiedMethods.length; n++) { + tls_wrap.TLSWrap.prototype[proxiedMethods[n]] = + makeMethodProxy(proxiedMethods[n]); +} tls_wrap.TLSWrap.prototype.close = function close(cb) { let ssl; From ebe8926025601fccfec9798b581f7f89e53a3526 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sat, 25 Feb 2017 22:52:16 -0800 Subject: [PATCH 04/11] fs: avoid using forEach --- lib/fs.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/fs.js b/lib/fs.js index 8a028bf79943e7..6487d39e4814d6 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -248,10 +248,11 @@ function statsFromValues() { } // Don't allow mode to accidentally be overwritten. -['F_OK', 'R_OK', 'W_OK', 'X_OK'].forEach(function(key) { - Object.defineProperty(fs, key, { - enumerable: true, value: constants[key] || 0, writable: false - }); +Object.defineProperties(fs, { + F_OK: {enumerable: true, value: constants.F_OK || 0}, + R_OK: {enumerable: true, value: constants.R_OK || 0}, + W_OK: {enumerable: true, value: constants.W_OK || 0}, + X_OK: {enumerable: true, value: constants.X_OK || 0}, }); function handleError(val, callback) { From 36dc38a027f1c4d051d0bb8dc620e363c0f3c61b Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sun, 26 Feb 2017 17:54:40 -0800 Subject: [PATCH 05/11] lib: avoid using forEach in LazyTransform --- lib/internal/streams/lazy_transform.js | 66 ++++++++++++++++---------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/lib/internal/streams/lazy_transform.js b/lib/internal/streams/lazy_transform.js index 3607d985346992..06cc87143d792a 100644 --- a/lib/internal/streams/lazy_transform.js +++ b/lib/internal/streams/lazy_transform.js @@ -14,31 +14,47 @@ function LazyTransform(options) { } util.inherits(LazyTransform, stream.Transform); -[ - '_readableState', - '_writableState', - '_transformState' -].forEach(function(prop, i, props) { - Object.defineProperty(LazyTransform.prototype, prop, { - get: function() { - stream.Transform.call(this, this._options); - this._writableState.decodeStrings = false; - - if (!this._options || !this._options.defaultEncoding) { - this._writableState.defaultEncoding = crypto.DEFAULT_ENCODING; - } - - return this[prop]; - }, - set: function(val) { - Object.defineProperty(this, prop, { - value: val, - enumerable: true, - configurable: true, - writable: true - }); - }, +function makeGetter(name) { + return function() { + stream.Transform.call(this, this._options); + this._writableState.decodeStrings = false; + + if (!this._options || !this._options.defaultEncoding) { + this._writableState.defaultEncoding = crypto.DEFAULT_ENCODING; + } + + return this[name]; + }; +} + +function makeSetter(name) { + return function(val) { + Object.defineProperty(this, name, { + value: val, + enumerable: true, + configurable: true, + writable: true + }); + }; +} + +Object.defineProperties(LazyTransform.prototype, { + _readableState: { + get: makeGetter('_readableState'), + set: makeSetter('_readableState'), + configurable: true, + enumerable: true + }, + _writableState: { + get: makeGetter('_writableState'), + set: makeSetter('_writableState'), + configurable: true, + enumerable: true + }, + _transformState: { + get: makeGetter('_transformState'), + set: makeSetter('_transformState'), configurable: true, enumerable: true - }); + } }); From abdd6c684fa18456ac0e39c3eb8fe302330d3819 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sun, 26 Feb 2017 18:08:04 -0800 Subject: [PATCH 06/11] net: avoid using forEach --- lib/net.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/net.js b/lib/net.js index fb9c72cb95240a..7690254042c7d5 100644 --- a/lib/net.js +++ b/lib/net.js @@ -1524,9 +1524,9 @@ Server.prototype.getConnections = function(cb) { if (--left === 0) return end(null, total); } - this._slaves.forEach(function(slave) { - slave.getConnections(oncount); - }); + for (var n = 0; n < this._slaves.length; n++) { + this._slaves[n].getConnections(oncount); + } }; @@ -1562,9 +1562,8 @@ Server.prototype.close = function(cb) { this._connections++; // Poll slaves - this._slaves.forEach(function(slave) { - slave.close(onSlaveClose); - }); + for (var n = 0; n < this._slaves.length; n++) + this._slaves[n].close(onSlaveClose); } else { this._emitCloseIfDrained(); } From e8738ca5b1bfed5ccd1accdd59e94619fc4be834 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sun, 26 Feb 2017 18:15:36 -0800 Subject: [PATCH 07/11] readline: avoid using forEach --- lib/readline.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/readline.js b/lib/readline.js index b5dab31a5f7f91..5237b258ed7731 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -392,9 +392,8 @@ Interface.prototype._normalWrite = function(b) { // either '' or (conceivably) the unfinished portion of the next line string = lines.pop(); this._line_buffer = string; - lines.forEach(function(line) { - this._onLine(line); - }, this); + for (var n = 0; n < lines.length; n++) + this._onLine(lines[n]); } else if (string) { // no newlines this time, save what we have for next time this._line_buffer = string; From 59d1fd69331fb33be25aec947058972dca38044e Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 27 Feb 2017 09:28:15 -0800 Subject: [PATCH 08/11] repl: avoid using forEach --- lib/repl.js | 63 ++++++++++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/lib/repl.js b/lib/repl.js index e4364e7d11cc42..2fe87a9cc658e6 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -68,7 +68,10 @@ const GLOBAL_OBJECT_PROPERTIES = [ 'ReferenceError', 'SyntaxError', 'TypeError', 'URIError', 'Math', 'JSON' ]; const GLOBAL_OBJECT_PROPERTY_MAP = {}; -GLOBAL_OBJECT_PROPERTIES.forEach((p) => GLOBAL_OBJECT_PROPERTY_MAP[p] = p); +for (var n = 0; n < GLOBAL_OBJECT_PROPERTIES.length; n++) { + GLOBAL_OBJECT_PROPERTY_MAP[GLOBAL_OBJECT_PROPERTIES[n]] = + GLOBAL_OBJECT_PROPERTIES[n]; +} try { // hack for require.resolve("./relative") to work properly. @@ -591,13 +594,17 @@ REPLServer.prototype.createContext = function() { enumerable: true, get: () => _console }); - Object.getOwnPropertyNames(global).filter((name) => { - if (name === 'console' || name === 'global') return false; - return GLOBAL_OBJECT_PROPERTY_MAP[name] === undefined; - }).forEach((name) => { - Object.defineProperty(context, name, - Object.getOwnPropertyDescriptor(global, name)); - }); + + var names = Object.getOwnPropertyNames(global); + for (var n = 0; n < names.length; n++) { + var name = names[n]; + if (name === 'console' || name === 'global') + continue; + if (GLOBAL_OBJECT_PROPERTY_MAP[name] === undefined) { + Object.defineProperty(context, name, + Object.getOwnPropertyDescriptor(global, name)); + } + } } const module = new Module(''); @@ -668,10 +675,8 @@ function ArrayStream() { Stream.call(this); this.run = function(data) { - var self = this; - data.forEach(function(line) { - self.emit('data', line + '\n'); - }); + for (var n = 0; n < data.length; n++) + this.emit('data', `${data[n]}\n`); }; } util.inherits(ArrayStream, Stream); @@ -715,11 +720,11 @@ function complete(line, callback) { var tmp = this.lines.slice(); // Kill off all function declarations to push all local variables into // global scope - this.lines.level.forEach(function(kill) { - if (kill.isFunction) { + for (var n = 0; n < this.lines.level.length; n++) { + var kill = this.lines.level[n]; + if (kill.isFunction) tmp[kill.line] = ''; - } - }); + } var flat = new ArrayStream(); // make a new "input" stream var magic = new REPLServer('', flat); // make a nested REPL replMap.set(magic, replMap.get(this)); @@ -853,9 +858,8 @@ function complete(line, callback) { addStandardGlobals(completionGroups, filter); } else if (Array.isArray(globals[0])) { // Add grouped globals - globals.forEach(function(group) { - completionGroups.push(group); - }); + for (var n = 0; n < globals.length; n++) + completionGroups.push(globals[n]); } else { completionGroups.push(globals); addStandardGlobals(completionGroups, filter); @@ -1161,12 +1165,13 @@ function defineDefaultCommands(repl) { (max, name) => Math.max(max, name.length), 0 ); - names.forEach((name) => { - const cmd = this.commands[name]; - const spaces = ' '.repeat(longestNameLength - name.length + 3); - const line = '.' + name + (cmd.help ? spaces + cmd.help : '') + '\n'; + for (var n = 0; n < names.length; n++) { + var name = names[n]; + var cmd = this.commands[name]; + var spaces = ' '.repeat(longestNameLength - name.length + 3); + var line = `.${name}${cmd.help ? spaces + cmd.help : ''}\n`; this.outputStream.write(line); - }); + } this.displayPrompt(); } }); @@ -1190,15 +1195,13 @@ function defineDefaultCommands(repl) { try { var stats = fs.statSync(file); if (stats && stats.isFile()) { - var self = this; var data = fs.readFileSync(file, 'utf8'); var lines = data.split('\n'); this.displayPrompt(); - lines.forEach(function(line) { - if (line) { - self.write(line + '\n'); - } - }); + for (var n = 0; n < lines.length; n++) { + if (lines[n]) + this.write(`${lines[n]}\n`); + } } else { this.outputStream.write('Failed to load:' + file + ' is not a valid file\n'); From e9e5c0ed435c333249c731340e803f4c8173aeae Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 27 Feb 2017 09:33:07 -0800 Subject: [PATCH 09/11] module: avoid using forEach --- lib/module.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/module.js b/lib/module.js index ef2b97322e3d2e..d9442a62b7c372 100644 --- a/lib/module.js +++ b/lib/module.js @@ -676,9 +676,8 @@ Module._preloadModules = function(requests) { throw e; } } - requests.forEach(function(request) { - parent.require(request); - }); + for (var n = 0; n < requests.length; n++) + parent.require(requests[n]); }; Module._initPaths(); From 2e0cb9130d8a5b6db07b3e361bc7ee8b41061b5c Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 27 Feb 2017 09:43:40 -0800 Subject: [PATCH 10/11] util: avoid using forEach --- lib/util.js | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/util.js b/lib/util.js index 31761f0dd35545..06d5aa5b20e425 100644 --- a/lib/util.js +++ b/lib/util.js @@ -674,12 +674,13 @@ function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { if (remaining > 0) { output.push(`... ${remaining} more item${remaining > 1 ? 's' : ''}`); } - keys.forEach(function(key) { + for (var n = 0; n < keys.length; n++) { + var key = keys[n]; if (typeof key === 'symbol' || !key.match(/^\d+$/)) { output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, key, true)); } - }); + } return output; } @@ -710,10 +711,10 @@ function formatSet(ctx, value, recurseTimes, visibleKeys, keys) { var str = formatValue(ctx, v, nextRecurseTimes); output.push(str); }); - keys.forEach(function(key) { + for (var n = 0; n < keys.length; n++) { output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, - key, false)); - }); + keys[n], false)); + } return output; } @@ -727,10 +728,10 @@ function formatMap(ctx, value, recurseTimes, visibleKeys, keys) { str += formatValue(ctx, v, nextRecurseTimes); output.push(str); }); - keys.forEach(function(key) { + for (var n = 0; n < keys.length; n++) { output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, - key, false)); - }); + keys[n], false)); + } return output; } @@ -760,10 +761,10 @@ function formatPromise(ctx, value, recurseTimes, visibleKeys, keys) { output.push(str); } } - keys.forEach(function(key) { + for (var n = 0; n < keys.length; n++) { output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, - key, false)); - }); + keys[n], false)); + } return output; } From 142bf54ba4e55a49320b49d87cb024cc51261b7f Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 27 Feb 2017 09:53:26 -0800 Subject: [PATCH 11/11] lib: avoid using forEach --- lib/internal/bootstrap_node.js | 65 +++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index 526cf7f3f64927..5ac8086e34c73d 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -206,14 +206,14 @@ global.process = process; const util = NativeModule.require('util'); - // Deprecate GLOBAL and root - ['GLOBAL', 'root'].forEach(function(name) { - // getter - const get = util.deprecate(function() { + function makeGetter(name) { + return util.deprecate(function() { return this; }, `'${name}' is deprecated, use 'global'`, 'DEP0016'); - // setter - const set = util.deprecate(function(value) { + } + + function makeSetter(name) { + return util.deprecate(function(value) { Object.defineProperty(this, name, { configurable: true, writable: true, @@ -221,8 +221,19 @@ value: value }); }, `'${name}' is deprecated, use 'global'`, 'DEP0016'); - // define property - Object.defineProperty(global, name, { get, set, configurable: true }); + } + + Object.defineProperties(global, { + GLOBAL: { + configurable: true, + get: makeGetter('GLOBAL'), + set: makeSetter('GLOBAL') + }, + root: { + configurable: true, + get: makeGetter('root'), + set: makeSetter('root') + } }); global.Buffer = NativeModule.require('buffer').Buffer; @@ -326,27 +337,31 @@ // With no argument, getVersion() returns a comma separated list // of possible types. const versionTypes = icu.getVersion().split(','); - versionTypes.forEach((name) => { - // Copied from module.js:addBuiltinLibsToObject + + function makeGetter(name) { + return () => { + // With an argument, getVersion(type) returns + // the actual version string. + const version = icu.getVersion(name); + // Replace the current getter with a new property. + delete process.versions[name]; + Object.defineProperty(process.versions, name, { + value: version, + writable: false, + enumerable: true + }); + return version; + }; + } + + for (var n = 0; n < versionTypes.length; n++) { + var name = versionTypes[n]; Object.defineProperty(process.versions, name, { configurable: true, enumerable: true, - get: () => { - // With an argument, getVersion(type) returns - // the actual version string. - const version = icu.getVersion(name); - // Replace the current getter with a new - // property. - delete process.versions[name]; - Object.defineProperty(process.versions, name, { - value: version, - writable: false, - enumerable: true - }); - return version; - } + get: makeGetter(name) }); - }); + } } function tryGetCwd(path) {