Nová syntaxe funkcí

Existuje ještě jeden způsob, jak vytvořit funkci. Používá se zřídka, ale někdy neexistuje žádná alternativa.

Syntaxe

Syntaxe pro vytvoření funkce:

let func = new Function ([arg1, arg2, ...argN], functionBody);

Funkce je vytvořena s argumenty arg1...argN a daný functionBody .

Je to snazší pochopit, když se podíváte na příklad. Zde je funkce se dvěma argumenty:

let sum = new Function('a', 'b', 'return a + b');

alert( sum(1, 2) ); // 3

A zde je funkce bez argumentů, pouze s tělem funkce:

let sayHi = new Function('alert("Hello")');

sayHi(); // Hello

Hlavním rozdílem od jiných způsobů, které jsme viděli, je to, že funkce je vytvořena doslova z řetězce, který je předán za běhu.

Všechny předchozí deklarace vyžadovaly, abychom my, programátoři, napsali kód funkce do skriptu.

Ale new Function umožňuje přeměnit libovolný řetězec na funkci. Můžeme například přijmout novou funkci ze serveru a poté ji spustit:

let str = ... receive the code from a server dynamically ...

let func = new Function(str);
func();

Používá se ve velmi specifických případech, jako když přijímáme kód ze serveru nebo k dynamické kompilaci funkce ze šablony ve složitých webových aplikacích.

Uzavření

Obvykle si funkce pamatuje, kde se zrodila ve speciální vlastnosti [[Environment]] . Odkazuje na lexikální prostředí, kde bylo vytvořeno (to jsme popsali v kapitole Variabilní rozsah, uzavření).

Ale když je funkce vytvořena pomocí new Function , jeho [[Environment]] je nastaven tak, aby neodkazoval na aktuální lexikální prostředí, ale na globální.

Taková funkce tedy nemá přístup k vnějším proměnným, pouze ke globálním.

function getFunc() {
 let value = "test";

 let func = new Function('alert(value)');

 return func;
}

getFunc()(); // error: value is not defined

Porovnejte to s běžným chováním:

function getFunc() {
 let value = "test";

 let func = function() { alert(value); };

 return func;
}

getFunc()(); // "test", from the Lexical Environment of getFunc

Tato speciální funkce new Function vypadá divně, ale v praxi se zdá být velmi užitečný.

Představte si, že musíme vytvořit funkci z řetězce. Kód této funkce není znám v době psaní skriptu (proto nepoužíváme běžné funkce), ale bude znám v procesu provádění. Můžeme jej obdržet ze serveru nebo z jiného zdroje.

Naše nová funkce potřebuje interakci s hlavním skriptem.

Co když má přístup k vnějším proměnným?

Problém je v tom, že než je JavaScript publikován do produkce, je komprimován pomocí minifikátoru – speciální program, který zmenšuje kód odstraněním nadbytečných komentářů, mezer a – co je důležité, přejmenovává lokální proměnné na kratší.

Například, pokud má funkce let userName , minifier jej nahradí let a (nebo jiné písmeno, pokud je toto obsazené), a dělá to všude. To je obvykle bezpečná věc, protože proměnná je lokální, nic mimo funkci k ní nemá přístup. A uvnitř funkce minifikátor nahradí každou zmínku o něm. Minifikátory jsou chytré, analyzují strukturu kódu, takže nic neporuší. Nejsou to jen hloupé hledání a výměny.

Pokud tedy new Function měl přístup k vnějším proměnným, nemohl by najít přejmenované userName .

Pokud new Function měl přístup k vnějším proměnným, měl by problémy s minifikátory.

Kromě toho by takový kód byl architektonicky špatný a náchylný k chybám.

Předat něco funkci vytvořené jako new Function , měli bychom použít jeho argumenty.

Shrnutí

Syntaxe:

let func = new Function ([arg1, arg2, ...argN], functionBody);

Z historických důvodů mohou být argumenty uvedeny také jako seznam oddělený čárkami.

Tyto tři deklarace znamenají totéž:

new Function('a', 'b', 'return a + b'); // basic syntax
new Function('a,b', 'return a + b'); // comma-separated
new Function('a , b', 'return a + b'); // comma-separated with spaces

Funkce vytvořené pomocí new Function , mají [[Environment]] odkazující na globální lexikální prostředí, nikoli na vnější. Nemohou tedy používat vnější proměnné. Ale to je vlastně dobře, protože nás to chrání před chybami. Explicitní předávání parametrů je architektonicky mnohem lepší metoda a nezpůsobuje žádné problémy s minifikátory.