Ich habe eine ziemlich zeitkritische Code in einer Reagieren-App, die auf allen Benutzeranschlägen in einem Formular ausgeführt wird. Es hat im Allgemeinen keine Leistungsprobleme, aber ich habe nach einer Optimierung gesucht und war ein wenig überrascht über die performance differences zwischen for (let k in obj)
und Object.keys(obj).reduce
.Prozedural vs. Functional Javascript
Ich würde denken, dass das Einrichten von Funktionsaufrufen etc. in JS teuer sein würde, aber die funktionale Version der folgenden Routine bläst die Prozedur aus dem Wasser (um eine volle Größenordnung!).
Hier sind die verschiedenen Versionen:
Procedural
const generateProps = (fields, source, start) => {
if (!fields) return start
let finish = {...start}
for (let k of Object.keys(fields)) {
const fld = fields[k]
if (fld instanceof Array) {
if (fld.length === 0) continue
// Handle an array of scalars (e.g. phoneNumbers)
if (fld[0].hasOwnProperty('value')) {
let sf = {}
for (let i = 0; i < fld.length; i++) {
sf[i] = fld[i]
}
finish = generateProps(sf, source[k], finish)
// Handle an array of fields (e.g. addresses)
} else {
for (let i = 0; i < fld.length; i++) {
finish = generateProps(fld[i], source[k][i], finish)
}
}
} else {
finish = {
hasError: fields[k].hasError || fields[k].value === '' || finish.hasError,
isEditing: fields[k].editing || finish.isEditing,
unchanged: (!fields[k].isNew && fields[k].value === source[k]) && finish.unchanged,
hasNew: fields[k].isNew || finish.hasNew
}
}
}
return finish
}
Funktionelle
const generateProps = (fields, source, start) => {
if (!fields) return start
const keys = Object.keys(fields)
return keys.reduce((props, k) => {
const fld = fields[k]
if (fld instanceof Array) {
if (fld.length === 0) return props
// Handle an array of scalars (e.g. phoneNumbers)
if (fld[0].hasOwnProperty('value')) return generateProps(fld.reduce((sf, f, i) => {sf[i] = f; return sf}, {}), source[k], props)
// Handle an array of fields (e.g. addresses)
return fld.reduce((subp, f, i) => generateProps(f, source[k][i], subp), props)
}
return {
hasError: fields[k].hasError || fields[k].value === '' || props.hasError,
isEditing: fields[k].editing || props.isEditing,
unchanged: (!fields[k].isNew && fields[k].value === source[k]) && props.unchanged,
hasNew: fields[k].isNew || props.hasNew
}
}, start)
}
Und hier sind die jperf results
Wie Sie sehen können, wenn Sie den Test ausführen, das Verfahren Version fast 50% langsamer als die funktionsfähig ist. Ich wäre daran interessiert zu hören, warum es so einen großen Unterschied gibt.
Wie so oft ist [der Benchmark] (https://jsperf.com/for-in-vs-for-of-keys-vs-keys-reduce), den Sie verlinkt haben, völlig fehlerhaft. – Bergi
Abgesehen davon, dass 'für (let ... in ...)' doppelt so schnell läuft wie die letzten beiden in Firefox .... und 'Object.keys (obj) .reduce' läuft doppelt so schnell wie die erste zwei in Chrome - Benchmarks in einem Browser bedeuten also eh nichts: p –
Welchen Browser benutzen Sie? '{... start}' ist ein Syntaxfehler im "procedural" jsperf. – Bergi