Legitimní užití evalu
Školení, která pořádám
Při odstraňování užití funkce eval
jsem narazil na zajímavý kód:
function objectConverter(columns) {
return new Function("d", "return {" + columns.map(function(name, i) {
return JSON.stringify(name) + ": d[" + i + "]";
}).join(",") + "}");
}
Funkce se používá pro vytváření objektů při importu CSV. V prvním řádku CSV jsou názvy sloupců, např. a, b, c
, tak tuto funkci zavoláme jako objectConverter(['a', 'b', 'c'])
. Ona vrátí takovouto funkci:
function(d) {
return {"a": d[0], "b": d[1], "c": d[2]};
}
Tuto funkci pak voláme s jednotlivými řádky CSV a ona nám vytváří objekty mapující název sloupce na jeho hodnotu.
Přepsat funkci objectConverter
tak, aby nepoužívala eval
je jednoduché:
function objectConverter(columns) {
return function(d) {
return columns.reduce(function(result, name, i) {
result[name] = d[i];
return result;
}, {});
};
}
Bohužel je tato funkce dramaticky pomalejší než originální implementace: 1462 ms místo 376 ms na testovacích datech v Chrome. Problém je nejspíš v tom, že jednotlivé řádky se vytváří vždycky postupně z prázdného objektu. Nepřišel jsem na to, jak funkci udělat stejně efektivní jako originální implementaci.
Diskuse
Díky za upozornění, opravil jsem to. Nějak jsem si odvykl psát XHTML.
Obecně jde o generování kódu, pak jsou podobné techniky přínosné. Vidím tu ale takový rule of thumb: Jak často je to potřeba? Pokud jen jednou při startu aplikace, pak je to nejspíš OK. (Nicméně by to pak šlo generovat staticky bez evalu…) Pokud častěji, stojí za zvážení, jestli tu není něco špatně.
Tahle konkrétní funkce se volá jednou při parsování každého CSV. Z prvního řádku, kde jsou nadpisy sloupců, se vytvoří funkce, která pak generuje objekty pro ostatní řádky.
Což už není úplně ideální. V některých případech se to do mého rule of thumb vleze (zpracování jednoho souboru), při opakovaném použití ne. Tam by se mělo (aspoň teoreticky) vyplatit mít funkci vyššího řádu, která vytvoří parsovací funkci.
Dá se tu aplikovat ještě jeden bezpečnostní rule of thumb: Jak velký vliv má na vstup potenciální útočník? Což se ale na druhou stranu bez kontextu okolního kódu dá spíše odhadovat.
Tohle právě je funkce vyššího řádu vytvářející konverzní funkci. Nejrychlejší je ovšem s evalem.
Tato funkce je očividně bezpečná. Žádný vstup nemůže vést ke spuštění jakéhokoliv kódu.
Problém je, že když z CSP vyhodíme 'unsafe-eval', tak žádný eval prostě nefunguje, i když bychom chtěli.
Zajímalo by mne, jestli by se nějak měřitelně změnil čas, pokud by se namísto funkcionální redukce použil cyklus.
Zkoušel jsem to taky, výsledek byl nepatrně lepší, ale pořád na míle daleko od evalu.
:o):
Neměl by se ten blog pomalu začít jmenovat JS triky? ;-)
Diskuse je zrušena z důvodu spamu.