Eval () e new Function () são a mesma coisa?

89

Essas duas funções estão fazendo a mesma coisa nos bastidores? (em funções de instrução única)

var evaluate = function(string) {
    return eval('(' + string + ')');
}

var func = function(string) {
    return (new Function( 'return (' + string + ')' )());
}

console.log(evaluate('2 + 1'));
console.log(func('2 + 1'));
qwertymk
fonte
3
Na verdade, isso é usado no jQuery 1.7.2 linha 573. Embora com "algumas verificações de segurança"
PicoCreator
2
Por que é newconsiderado nesta discussão? Functioninstancia implicitamente a function object. A exclusão newnão mudará o código de forma alguma. Aqui está um jsfiddle demonstrando que: jsfiddle.net/PcfG8
Travis J

Respostas:

114

Não, eles não são os mesmos.

  • eval() avalia uma string como uma expressão JavaScript dentro do escopo de execução atual e pode acessar variáveis ​​locais.
  • new Function()analisa o código JavaScript armazenado em uma string em um objeto de função, que pode então ser chamado. Ele não pode acessar variáveis ​​locais porque o código é executado em um escopo separado.

Considere este código:

function test1() {
    var a = 11;
    eval('(a = 22)');
    alert(a);            // alerts 22
}

Se new Function('return (a = 22);')()fosse usada, a variável local amanteria seu valor. No entanto, alguns programadores de JavaScript, como Douglas Crockford, acreditam que nenhum dos dois deve ser usado a menos que seja absolutamente necessário , e avaliar / usar o Functionconstrutor em dados não confiáveis ​​é inseguro e imprudente.

Fique por favor
fonte
10
"nunca deve ser usado" é uma declaração tão ampla. Que tal, nunca deve ser usado quando alguma das entradas está fora de seu controle. Dito isso, nunca precisei usá-lo, exceto para analisar JSON. Mas respondi a perguntas aqui que pareciam utilizações válidas, envolvendo algo como permitir que os administradores gerassem funções de modelos que os usuários finais poderiam usar. Neste caso, a entrada foi gerada por um administrador, então foi aceitável
Juan Mendes
3
@Juan: O ponto de Crockford é que eval costuma ser mal utilizado (depende da sua perspectiva), portanto, deve ser excluído de um JavaScript de "melhores práticas". Eu concordo, no entanto, que é útil para usos como JSON ou análise de expressão aritmética quando a entrada é devidamente validada pela primeira vez. Até Crockford o usa em sua biblioteca JSON json2.js.
favor
Então, isso significa que new Function(code)é o mesmo eval('(function(){'+code+'})()')ou é um contexto de execução totalmente novo?
George Mauer
5
@GeorgeMauer: Acho que você quer dizer eval('(function(){'+code+'})'), que retornaria um objeto de função. De acordo com o MDN , "as funções criadas com o construtor Function não criam fechamentos para seus contextos de criação; elas sempre são executadas no contexto da janela (a menos que o corpo da função comece com uma instrução" use strict ", caso em que o contexto é indefinido) . "
favor
6

Não.

Em sua atualização, as chamadas para evaluatee funcproduzem o mesmo resultado. Mas, eles definitivamente não estão "fazendo a mesma coisa nos bastidores". A funcfunção cria uma nova função, mas a executa imediatamente, enquanto a evaluatefunção simplesmente executa o código no local.

Da pergunta original:

var evaluate = function(string) {
    return eval(string);
}
var func = function(string) {
    return (new Function( 'return (' + string + ')' )());
}

Isso lhe dará resultados muito diferentes:

evaluate('0) + (4');
func('0) + (4');
palswim
fonte
Em qualquer caso, ambos são uma má prática em todos, exceto nos casos mais excepcionais.
palswim
8
Bem, você falsificou seu exemplo de acordo com sua implementação. Se ele tivesse implementado a avaliação return eval('(' + string + ')');, eles produziriam os mesmos resultados.
Juan Mendes
@Juan Eu não pensei, mas sim. Edição da pergunta
qwertymk
6

new Functioncria uma função que pode ser reutilizada. evalapenas executa a string fornecida e retorna o resultado da última instrução. Sua pergunta está equivocada, pois você tentou criar uma função de wrapper que usa Function para emular um eval.

É verdade que eles compartilham algum código por trás das cortinas? Sim, muito provavelmente. Exatamente o mesmo código? Certamente.

Por diversão, aqui está minha própria implementação imperfeita usando eval para criar uma função. Espero que esclareça a diferença!

function makeFunction() {
  var params = [];
  for (var i = 0; i < arguments.length -  1; i++) {
    params.push(arguments[i]);
  }
  var code = arguments[arguments.length -  1];


 // Creates the anonymous function to be returned
 // The following line doesn't work in IE
 // return eval('(function (' + params.join(',')+ '){' + code + '})');
 // This does though
 return eval('[function (' + params.join(',')+ '){' + code + '}][0]');
}

A maior diferença entre esta e a nova Função é que Function não tem escopo léxico. Portanto, ele não teria acesso às variáveis ​​de fechamento e o meu teria.

Juan Mendes
fonte
por que não usar em arguments.slice()vez do loop for? Ou se os argumentos não forem uma matriz verdadeira [].slice.call(arguments, 1),.
Xeoncross de
3

Quero apenas apontar algumas sintaxes usadas nos exemplos aqui e o que isso significa:

 var func = function(string) {
     return (new Function( 'return (' + string + ')' )());
 }

observe que Function (...) () tem o "()" no final. Essa sintaxe fará com que func execute a nova função e retorne a string, não uma função que retorne string, mas se você usar o seguinte:

 var func = function(string) {
     return (new Function( 'return (' + string + ')' ));
 }

Agora func retornará uma função que retorna uma string.

Jack D Menendez
fonte
2

Se você quer dizer, ele produzirá os mesmos resultados, então sim ... mas apenas avaliar (também conhecido como "avaliar esta string de JavaScript") seria muito mais simples.

EDITAR abaixo:

É como dizer ... esses dois problemas matemáticos são iguais:

1 + 1

1 + 1 + 1 - 1 + 1 - 1 * 1/1

Timothy Khouri
fonte
ambos analisam novamente a string?
qwertymk
ambos irão analisar a string (em seu exemplo de código). não sei o que você quer dizer com "re-analisar".
Timothy Khouri
1
Tenho certeza de que ambos analisam (avaliam) a string em algum ponto, mas o Functionobjeto provavelmente armazena a string em uma variável de membro privada até evalquando você invoca a função.
palswim
1

Nesse exemplo, os resultados são os mesmos, sim. Ambos executam a expressão que você passa. Isso é o que os torna tão perigosos.

Mas eles fazem coisas diferentes por trás do scense. O que envolve new Function(), nos bastidores, cria uma função anônima a partir do código que você fornece, que é executada quando a função é chamada.

O JavaScript que você passa para ele não é tecnicamente executado até que você invoque a função anônima. Isso contrasta com o eval()que executa o código imediatamente e não gera uma função baseada nele.

Chris Laplante
fonte
Você pode expor um pouco mais? Ambos não são executados na instrução de retorno? A função Function () usa outro nível de chamada de pilha diferente de eval?
qwertymk
Does the Function() one use another stack call level than eval?: Eu diria que sim, porque eval()não cria uma função a partir de sua entrada - apenas a executa. new Function()cria uma nova função, que então invoca.
Chris Laplante
@SimpleCoder: Function () não adiciona nada à pilha de chamadas. Somente se você chamar a função resultante. eval faz exatamente a mesma coisa (se aplicado no contexto da questão)
Juan Mendes