• Jetzt anmelden. Es dauert nur 2 Minuten und ist kostenlos!

Function, um eine Funktion aus einem String auszuführen crasht beim 2. Aufruf

SKB

Neues Mitglied
Hallo,
ich habe eine Javascript Function, welche aus einem Object (over) Zahlen oder Strings als Keys vergleicht und das beste "Match" herauszieht.
Dieses Match enthält meistens 1 oder mehrere Properties, die sowohl String, Zahlen, aber auch Strings als Functions enthalten können.

Wenn ich die Funktion einmal Aufrufe, funktioniert sie problemlos - sobald ich sie aber ein 2. Mal aufrufe, werden einfach nur die Werte vom vorherigen Aufruf übernommen. Könnte mir jemand auf den Sprung helfen? Ich habe irgendwie das Gefühl, das es etwas mit den "new Function" auf sich hat.

Vielen Dank!

Javascript:
const over = {
            ">=22": {
                "fill": "#FF0000",
                "value": "val => Number(Number(5)+Number(5))+Number(10)+val",
                "unit": "val => val >= 1000 ? 'kW' : 'W'"
            },
            "default": {
                "fill": "#FFFFFF",
                "value": "val => parseFloat(val/1000).toFixed(2)",
                "unit": "val => Math.abs(val) > 1000 ? 'kW' : 'W'"
            }
        };
        /**
             *
             * @param {string} id
             * @param {number} condValue
             * @param {object} object
             * @returns {object} result
             */
        // Testing for Override
        function getOverrides(id, condValue, object) {
            let result = {};
            const workObj = typeof (object) === 'string' ? JSON.parse(object) : object;
            if (workObj.hasOwnProperty(condValue)) {
                // Check, if Property exists - if yes, directly return it, because thats the best match
                result = workObj[condValue];
            } else {
                // Property not found. We need to check the values!
                const operators = new RegExp('[=><!]');
                Object.keys(workObj)
                    .sort(
                        (a, b) => a.toLowerCase().localeCompare(b.toLowerCase(), undefined, { numeric: true, sensitivity: 'base' }))
                    .forEach((item) => {
                        if (operators.test(item)) {
                            // Now, we need to check, if condValue is a number
                            if (!isNaN(condValue)) {
                                // Operator found - check for condition
                                try {
                                    const func = Function(`return ${condValue}${item}`)();
                                    if (func) {
                                        result = workObj[item];
                                    }
                                }
                                catch (func) {
                                    result = workObj[item];
                                    result.error = {
                                        status: false,
                                        error: func.toString(),
                                        func: condValue
                                    }
                                }
                            }
                        }
                    });
            }

            if (Object.keys(result).length == 0) {
                // Check, if we have a fallback for it
                if (workObj.hasOwnProperty('default')) {
                    result = workObj['default'];
                }
            }

            // Process the result values, if available
            if (Object.keys(result).length > 0) {
                // Value
                if (result.hasOwnProperty('value')) {
                    try {
                        const valueFunc = new Function("return " + result.value)();
                        result.value = valueFunc(condValue);
                    }
                    catch (valueFunc) {
                        result.value = result.value;
                        result.error = {
                            status: false,
                            error: valueFunc.toString(),
                            function: result.value
                        };
                    }
                }

                // Unit
                if (result.hasOwnProperty('unit')) {
                    try {
                        const unitFunc = new Function(`return ${result.unit}`)();
                        result.unit = unitFunc(condValue);
                    }
                    catch (unitFunc) {
                        result.unit = result.unit;
                        result.error = {
                            status: false,
                            error: unitFunc.toString(),
                            function: result.unit
                        };
                    }
                }
            }
            return result;
        }

        console.log(getOverrides(1, 50, over));
        console.log(getOverrides(2, 1000, over));
 
Werbung:
Hallo @SKB

Ist ganz schon verschachtelt, was du da erstellt hast.
Ich denke dein Problem sind nicht new Function Teile, sondern der
result = workObj[item]; Teil.

Dort erstellst du eine Referenz auf ein Value aus deinem over Objekt.
Jeder spätere Aufruf von result = /* irgendwas */ verändert dann dein ursprüngliches Objekt.
Damit sind deine Daten für den zweiten Aufruf verändert und dadurch hat sich dann auch dein Ergebnis verändert.

Ich würde vorschlagen, das Objekt in Zeile 41 nicht zu referenzieren sonder zu kopieren.
über structuredClone, Object.assign, den JSON.parse(JSON.stringify()) - Trick, etc. (Je nach Engine und Tiefe bzw. Inhalt deines Objektes).


Grüße
Andreas

PS: für die Zeile 45 gilt das gleiche.
 
  • Like
Reaktionen: SKB
Danke, was ich aber irgendwie nicht verstehe - ich übergebe das Objekt "over" an die Funktion.

Wieso bleibt es nicht "Original"?
 
Werbung:
"Objekte" und Objekttypen (object, array, ...) werden in JavaScript immer als Referenz übergeben. (Man kann das auch nicht beeinflussen.), was bedeutet, das eine Änderung immer das Original mit ändert.
"Primitive Typen" (wie zB number, string, boolean ...) werden immer as Value übergeben was bedeutet, das man dort durch eine Änderung nicht das Original ändert.

Was mir gerade einfällt, du kannst das Kopieren des Objekts natürlich auch vor dem Aufruf der Funktion machen, dann musst du aber die Tiefe deines Objektes bedenken, da zB die Object.assign Methode nur eine "flache" Kopie erstellt, wo die Objekte darinnen immer noch Referenzen zum Original haben.
 
  • Like
Reaktionen: SKB
Hast du vielleicht ein Beispiel, wie ich das übergebene Objekt einfach in die Funktion bekomme und damit arbeiten kann, ohne das Original zu beeinflussen?
 
Wie gesagt, das kommt auf das Objekt und auf deine Engine (nodeJs, Browser, ...) drauf an.
Welche Engine verwendest du denn?

Wenn es deine Engine erlaubt ist const copy = structuredClone(over) ein sauberer Weg.
Wenn nicht und dein Objekt enhält keine Funktionen oder Referenzen auf andere Werte ist const copy = JSON.parse(JSON.stringify(over)) ein viel verwendeter Weg.
 
  • Like
Reaktionen: SKB
Werbung:
Prima, danke!

Ich habe die Funktion nochmal ein wenig überarbeitet und optimiert.

Wenn Du nochmal schauen möchtest, ob dir etwas Signifikantes auffällt?!

Javascript:
function getOverrides(id, condValue, obj) {
            let tmpWorker = {};
            const workObj = typeof (obj) === 'string' ? JSON.parse(obj) : JSON.parse(JSON.stringify(obj));

            if (workObj.hasOwnProperty(condValue)) {
                // Check, if Property exists - if yes, directly return it, because thats the best match
                tmpWorker = workObj[condValue];
            } else {
                // Property not found. We need to check the values!
                const operators = new RegExp('[=><!]');
                Object.keys(workObj)
                    .sort(
                        (a, b) => a.toLowerCase().localeCompare(b.toLowerCase(), undefined, { numeric: true, sensitivity: 'base' }))
                    .forEach((item) => {
                        if (operators.test(item)) {
                            // Now, we need to check, if condValue is a number
                            if (!isNaN(condValue)) {
                                // Operator found - check for condition
                                try {
                                    const func = Function(`return ${condValue}${item}`)();
                                    if (func) {
                                        tmpWorker = workObj[item];
                                    }
                                }
                                catch (func) {
                                    tmpWorker.error = {
                                        status: false,
                                        error: func.toString(),
                                        func: condValue
                                    }
                                }
                            }
                        }
                    });
            }

            if (Object.keys(tmpWorker).length == 0) {
                // Check, if we have a default fallback for it
                if (workObj.hasOwnProperty('default')) {
                    tmpWorker = workObj['default'];
                }
            }

            // Now we process the found values inside tmpWorker Obj
            if (Object.keys(tmpWorker).length > 0) {
                Object.keys(tmpWorker).forEach(item => {
                    try {
                        const func = new Function(`return ${tmpWorker[item]}`)();
                        tmpWorker[item] = func(condValue);
                    }
                    catch (func) {
                        tmpWorker[item] = tmpWorker[item];
                    }
                });
            }
            return tmpWorker;
        }
 
Ganz herzlichen Dank für den Tipp mit der Referenz.
Das war mir irgendwie gar nicht klar
 
Werbung:
Zurück
Oben