2

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.

+1

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

+0

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 –

+0

Welchen Browser benutzen Sie? '{... start}' ist ein Syntaxfehler im "procedural" jsperf. – Bergi

Antwort

0

Ok, ich habe einen entscheidenden Unterschied gefunden! Die funktionale Version mutiert start (ich hatte angenommen, reduzieren würde eine Kopie erstellen, die von einem ramdajs Hintergrund stammt), während das Verfahren eine Kopie erstellt. Wenn ich das letzte Argument des obersten reduce Aufrufs zu {...start} ändere, sind sie ungefähr gleich.

Was mich wundern lässt ... Warum ist das Objekt verteilt langsam?

Verwandte Themen