Czy eval () i new Function () to to samo?

92

Czy te dwie funkcje robią to samo za kulisami? (w funkcjach pojedynczych instrukcji)

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
źródło
3
W rzeczywistości jest to używane w jQuery 1.7.2 linii 573. Chociaż z "pewnymi kontrolami bezpieczeństwa"
PicoCreator
2
Dlaczego jest to newrozważane w tej dyskusji? Functionniejawnie tworzy wystąpienie pliku function object. Wykluczenie w newogóle nie zmieni kodu. Oto jsfiddle demonstrujące, że: jsfiddle.net/PcfG8
Travis J

Odpowiedzi:

117

Nie, to nie to samo.

  • eval() ocenia ciąg jako wyrażenie JavaScript w bieżącym zakresie wykonania i może uzyskać dostęp do zmiennych lokalnych.
  • new Function()analizuje kod JavaScript przechowywany w ciągu znaków na obiekt funkcji, który można następnie wywołać. Nie może uzyskać dostępu do zmiennych lokalnych, ponieważ kod działa w oddzielnym zakresie.

Rozważ ten kod:

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

Gdyby new Function('return (a = 22);')()została użyta, zmienna lokalna azachowałaby swoją wartość. Niemniej jednak niektórzy programiści JavaScript, tacy jak Douglas Crockford, uważają, że nie należy ich używać, chyba że jest to absolutnie konieczne , a ewaluacja / używanie Functionkonstruktora na niezaufanych danych jest niebezpieczne i nierozsądne.

Proszę wstać
źródło
11
„nigdy nie powinno być używane” to tak szerokie stwierdzenie. Co powiesz na to, nigdy nie należy go używać, gdy jakiekolwiek wejście jest poza twoją kontrolą. Powiedziawszy to, nigdy nie musiałem go używać, z wyjątkiem parsowania JSON. Ale odpowiedziałem tutaj na pytania, które wydawały się ważnymi zastosowaniami, obejmowały coś takiego, jak pozwolenie administratorom na generowanie funkcji szablonów, z których mogliby korzystać użytkownicy końcowi. W tym przypadku dane wejściowe zostały wygenerowane przez administratora, więc było to akceptowalne
Juan Mendes
3
@Juan: Crockford twierdzi, że eval jest często nadużywany (w zależności od twojego punktu widzenia), więc powinien zostać wyłączony z „najlepszych praktyk” JavaScript. Zgadzam się jednak, że jest to przydatne do takich zastosowań, jak analiza JSON lub wyrażeń arytmetycznych, gdy dane wejściowe są najpierw poprawnie sprawdzane. Nawet Crockford używa go w swojej bibliotece JSON json2.js.
odstawić
Czy to oznacza, że new Function(code)jest to to samo, eval('(function(){'+code+'})()')czy jest to zupełnie nowy kontekst wykonania?
George Mauer
5
@GeorgeMauer: Myślę, że masz na myśli eval('(function(){'+code+'})'), co zwróci obiekt funkcji. Według MDN , „Funkcje utworzone za pomocą konstruktora Function nie tworzą domknięć do swoich kontekstów tworzenia; zawsze działają w kontekście okna (chyba że treść funkcji zaczyna się od instrukcji„ use strict ”; w takim przypadku kontekst jest niezdefiniowany) ”.
wstać
6

Nie.

W twojej aktualizacji wywołania evaluatei funcdają ten sam wynik. Ale z całą pewnością nie „robią tego samego za kulisami”. funcFunkcja tworzy nową funkcję, ale potem natychmiast wykonuje go, podczas gdy evaluatefunkcja po prostu wykonuje kod na miejscu.

Z pierwotnego pytania:

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

Dają one bardzo różne wyniki:

evaluate('0) + (4');
func('0) + (4');
palswim
źródło
W każdym razie obie są złą praktyką we wszystkich, z wyjątkiem najbardziej wyjątkowych przypadków.
palswim,
8
Cóż, sfałszowałeś swój przykład zgodnie z jego implementacją. Gdyby wdrożył ewaluację, jak return eval('(' + string + ')');wtedy, przyniosłyby takie same wyniki.
Juan Mendes,
@Juan Nie pomyślałem, ale tak.
Edytował
6

new Functiontworzy funkcję, której można użyć ponownie. evalpo prostu wykonuje podany ciąg i zwraca wynik ostatniej instrukcji. Twoje pytanie jest błędne, gdy próbowałeś utworzyć funkcję opakowującą, która używa funkcji do emulacji wartości eval.

Czy to prawda, że ​​za zasłonami mają wspólny kod? Tak, bardzo prawdopodobne. Dokładnie ten sam kod? Nie, na pewno.

Dla zabawy, oto moja własna niedoskonała implementacja wykorzystująca eval do tworzenia funkcji. Mam nadzieję, że rzuca trochę światła na różnicę!

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]');
}

Największą różnicą między tą a nową funkcją jest to, że funkcja nie ma zakresu leksykalnego. Więc nie miałby dostępu do zmiennych zamykających, a mój miałby.

Juan Mendes
źródło
dlaczego nie użyć arguments.slice()zamiast pętli for? Lub jeśli argumenty nie jest prawdziwym tablicą [].slice.call(arguments, 1).
Xeoncross,
3

Chcę tylko wskazać jakąś składnię używaną w przykładach tutaj i co to oznacza:

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

zwróć uwagę, że funkcja (...) () ma na końcu znak „()”. Ta składnia spowoduje, że func wykona nową funkcję i zwróci ciąg, a nie funkcję zwracającą ciąg, ale jeśli użyjesz następującego:

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

Teraz func zwróci funkcję, która zwraca ciąg.

Jack D. Menendez
źródło
2

Jeśli masz na myśli, czy przyniesie to te same wyniki, to tak ... ale samo oszacowanie (inaczej „oszacuj ten ciąg JavaScript”) byłoby znacznie prostsze.

EDYTUJ poniżej:

To tak, jakby powiedzieć ... czy te dwa zadania matematyczne są takie same:

1 + 1

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

Timothy Khouri
źródło
czy obaj ponownie analizują ciąg?
qwertymk
oboje przeanalizują ciąg (w przykładowym kodzie). nie wiem, co masz na myśli, mówiąc „ponownie przeanalizuj”.
Timothy Khouri,
1
Jestem pewien, że w pewnym momencie oboje analizują (oceniają) ciąg, ale Functionobiekt prawdopodobnie przechowuje ciąg w prywatnej zmiennej evalskładowej, aby wywołać funkcję.
palswim,
1

W tym przykładzie wyniki są takie same, tak. Oba wykonują przekazane wyrażenie. To właśnie czyni je tak niebezpiecznymi.

Ale robią różne rzeczy za tym poczuciem. Ten new Function(), który jest zakulisowy, tworzy anonimową funkcję na podstawie dostarczonego kodu, która jest wykonywana, gdy funkcja jest wywoływana.

JavaScript, który do niego przekazujesz, technicznie nie jest wykonywany, dopóki nie wywołasz funkcji anonimowej. W przeciwieństwie do tego, eval()który wykonuje kod od razu i nie generuje funkcji na jego podstawie.

Chris Laplante
źródło
Czy możesz wyjaśnić trochę więcej? Czy nie są one wykonywane w instrukcji return? Czy funkcja () używa innego poziomu wywołań stosu niż eval?
qwertymk
Does the Function() one use another stack call level than eval?: Tak przypuszczam, ponieważ eval()nie tworzy funkcji na podstawie danych wejściowych - po prostu ją wykonuje. new Function()tworzy nową funkcję, którą następnie wywołuje.
Chris Laplante,
@SimpleCoder: Funkcja () nie dodaje niczego do stosu wywołań. Tylko jeśli wywołasz wynikową funkcję. eval robi dokładnie to samo (jeśli zastosuje się go w kontekście pytania)
Juan Mendes