Existe mais uma maneira de criar uma função. Raramente é usado, mas às vezes não há alternativa.
A sintaxe para criar uma função:
deixe func = nova Função ([arg1, arg2, ...argN], functionBody);
A função é criada com os argumentos arg1...argN
e o functionBody
fornecido.
É mais fácil entender olhando um exemplo. Aqui está uma função com dois argumentos:
deixe soma = new Function('a', 'b', 'return a + b'); alerta(soma(1, 2) ); //3
E aqui está uma função sem argumentos, apenas com o corpo da função:
deixe dizerOi = new Function('alert("Olá")'); digaOi(); // Olá
A principal diferença de outras formas que vimos é que a função é criada literalmente a partir de uma string, que é passada em tempo de execução.
Todas as declarações anteriores exigiam que nós, programadores, escrevêssemos o código da função no script.
Mas new Function
permite transformar qualquer string em uma função. Por exemplo, podemos receber uma nova função de um servidor e então executá-la:
deixe str = ... receber o código de um servidor dinamicamente ... deixe func = nova Função (str); função();
É usado em casos muito específicos, como quando recebemos código de um servidor, ou para compilar dinamicamente uma função a partir de um template, em aplicações web complexas.
Normalmente, uma função lembra onde nasceu na propriedade especial [[Environment]]
. Ele faz referência ao Ambiente Lexical de onde foi criado (abordamos isso no capítulo Escopo da variável, fechamento).
Mas quando uma função é criada usando new Function
, seu [[Environment]]
é definido para fazer referência não ao ambiente lexical atual, mas ao global.
Portanto, tal função não tem acesso às variáveis externas, apenas às globais.
function getFunc() { deixe valor = "teste"; deixe func = new Function('alert(valor)'); função de retorno; } getFunc()(); //erro: o valor não está definido
Compare-o com o comportamento normal:
function getFunc() { deixe valor = "teste"; deixe func = function() { alert(valor); }; função de retorno; } getFunc()(); // "test", do Ambiente Lexical de getFunc
Este recurso especial da new Function
parece estranho, mas parece muito útil na prática.
Imagine que devemos criar uma função a partir de uma string. O código dessa função não é conhecido no momento da escrita do script (é por isso que não usamos funções regulares), mas será conhecido no processo de execução. Podemos recebê-lo do servidor ou de outra fonte.
Nossa nova função precisa interagir com o script principal.
E se pudesse acessar as variáveis externas?
O problema é que antes de o JavaScript ser publicado para produção, ele é compactado usando um minificador – um programa especial que reduz o código removendo comentários extras, espaços e – o que é importante, renomeia variáveis locais para variáveis mais curtas.
Por exemplo, se uma função let userName
, o minifier a substitui por let a
(ou outra letra se esta estiver ocupada) e faz isso em todos os lugares. Geralmente é algo seguro a se fazer, porque a variável é local e nada fora da função pode acessá-la. E dentro da função, o minifier substitui todas as menções a ela. Os minificadores são inteligentes, analisam a estrutura do código e não quebram nada. Eles não são apenas uma ferramenta idiota para localizar e substituir.
Portanto, se new Function
tivesse acesso a variáveis externas, não seria possível encontrar userName
renomeado.
Se new Function
tivesse acesso a variáveis externas, teria problemas com minificadores.
Além disso, esse código seria arquitetonicamente ruim e propenso a erros.
Para passar algo para uma função, criada como new Function
, devemos usar seus argumentos.
A sintaxe:
deixe func = nova Função ([arg1, arg2, ...argN], functionBody);
Por razões históricas, os argumentos também podem ser apresentados como uma lista separada por vírgulas.
Estas três declarações significam o mesmo:
nova Função('a', 'b', 'retornar a + b'); //sintaxe básica nova Função('a,b', 'retornar a + b'); //separados por vírgula nova Função('a, b', 'retornar a + b'); //separados por vírgulas com espaços
Funções criadas com new Function
, têm [[Environment]]
referenciando o Ambiente Lexical global, não o externo. Portanto, eles não podem usar variáveis externas. Mas isso é realmente bom, porque nos protege contra erros. Passar parâmetros explicitamente é um método muito melhor do ponto de vista arquitetônico e não causa problemas com minificadores.