JSON.parse()和eval()的区别

3

这可能需要一位JavaScript语言方面的专家:

var s1 = "{\"x\":\"y:z\"}"

var o = JSON.parse(s1)
var s2 = JSON.stringify(o)

$('span#s').text(s1);
$('span#s2').text(s2);

if (s1 === s2) {
    $('span#areEqual').text('s1 === s2')
} else {
    $('span#areEqual').text('s1 !== s2')
}

JSON.parse(s2) // okay

$('span#jsonParse').text("JSON.parse(s2) okay")

eval(s2) // bad mojo!

$('span#eval').text("eval(s2) okay")

eval("("+s2+")") // bad mojo, too! 
$('span#eval2').text("eval((s2)) okay")
< p > evals1s2"("+s2+")" 上均失败。

jsFiddle 的链接在这里


1
如果你想知道为什么eval会出现SyntaxError,那是因为该字符串不代表一个有效的JavaScript程序,而这正是eval所需要的。 - cookie monster
1
@JeffLowery:不,JSON.parse()是一个JSON解析器。它接受有效的JSON,并不关心它是否是有效的JS程序。 - cookie monster
2
@JeffLowery:不是的。“文本必须用括号括起来,以避免在JavaScript语法中出现歧义。” - cookie monster
1
@JeffLowery:与您的说法相反,eval("("+s2+")")并不会失败。 - Chuck
1
当你把一个用括号包裹的放在已经失败的下面时,执行会在出现错误时停止。 - cookie monster
显示剩余16条评论
7个回答

6
你的问题是混淆了两个不相关的东西。
eval()是内置的JavaScript函数,其主要目的是解释JavaScript代码字符串(因此可能存在安全漏洞)。
JSON.parse()函数用于解析JSON字符串。虽然非常相似,但不要犯错,JSON不是JavaScript,而且有微小的差异。你不应该使用eval()来解析JSON。 JSON和JavaScript对象之间有什么区别?

1
我认为这不仅仅是“微小的差异”。 - Evan Trimboli
这是一个很好的解释,但它与“... JSON是JavaScript的一个合适子集”(www.json.org)不符。 - Jeff Lowery
好的,确切的引用是“它基于JavaScript编程语言的一个子集”...“基于”并不等同于“是一个适当的子集”,在我看来。 - apocalypz

2
$eval会自动针对给定作用域进行评估。
例如:
$scope.a = 2;
var result = $scope.$eval('1+1+a');
// result is 4

$parse不需要作用域(scope)。它以表达式为参数并返回一个函数。该函数可以被调用,并使用能够解析局部变量的对象:

例如:

var fn = $parse('1+1+a');
var result = fn({ a: 2 });
// result is 4

1

当您使用eval解析JSON时,需要用括号将表达式包起来。

eval('(' + s2 + ')');

jsfiddle


做了那个,没有任何区别,它还是失败了。 - Jeff Lowery

1

查看规范中关于JSON和eval的说明http://www.json.org/js.html,特别注意以下部分

eval函数非常快。但是,它可以编译和执行任何JavaScript程序,因此可能存在安全问题。只有在源受信任且能力强时才建议使用eval。使用JSON解析器要安全得多。在XMLHttpRequest上的Web应用程序中,通信仅允许与提供该页面的相同来源进行,因此是受信任的。但它可能不够能力强。如果服务器在其JSON编码方面不严格,或者如果它没有仔细验证所有输入,则可能会提供无效的JSON文本,这可能携带危险脚本。eval函数将执行该脚本,释放其恶意。

JSON只是JavaScript对象,不再是其他东西。有效的JavaScript可能包括函数、执行块等。如果你只是 eval() 一个字符串,它可能包含代码。JSON将解析如果它只是JSON,但你不能确定只是把它塞进eval中。例如

var s = "(function(){ /* do badStuff */ return {s: 123, t: 456}; })()";
var result = eval(s);

这会为您提供一个名为 result 的变量,其内容为 {s: 123, t: 456},但也会执行您函数中隐藏的任何代码。如果您从其他地方获取此输入,则代码可能正在执行,但不会实际破坏您的程序。现在使用JSON.parse的相同示例。

var result = JSON.parse(s);

它抛出一个带有消息的错误:
Uncaught SyntaxError: Unexpected token ( 

所以解析器在这里保护您免受远程代码执行的攻击,即使有人试图将其偷偷插入。

是的,好建议。我正在快速制作一个原型,并且长期以来一直依赖于从$.ajax直接获取指定为JSON类型的JSON。我很少使用eval(),但它是我想到的第一件事。糟糕的eval。 - Jeff Lowery

0

eval不是一个表达式 - 我已经更新它以评估eval(s2 === s1); 否则它将尝试执行eval内部的内容并停止执行。


0

eval()函数尝试评估一段JavaScript代码。如果您创建了一个以相同文本开头的脚本文件,您将会得到相同的错误。在那种情况下,我认为大括号表示一个复合语句,例如if语句或for语句体,但在复合语句的开头是一个字符串,后面跟着一个冒号,这不是有效的语法。

如果您想要一个评估为对象的字符串,您必须将对象表达式括在括号中,以明确它是一个表达式。但正如apocalypz所说,您不应该尝试评估JSON。这在很多方面都是错误的。


我不反对不使用eval(),但是试图理解似乎存在冲突的信息。看起来发生的事情是JSON.parse正在清理输入。转义引号使eval解析变得混乱,因为它看到了这样的东西:{x:y:z}。 - Jeff Lowery
@JeffLowery:不,那不是发生的事情。只需使用 "{\"x\":1}" 就会得到相同的结果。我在这个回答中解释了正在发生的事情 - 它根本没有被解释为对象表达式。为了使它被解析为对象表达式,您需要用括号将其括起来。 - Chuck
这不是一个Javascript对象?但对于JSON.parse()函数来说,它看起来像一个对象:> var s1 = "{\"x\":\"y:z\"}" undefined > var o = JSON.parse(s1) undefined > o Object {x: "y:z"} - Jeff Lowery
@JeffLowery:你是来理解还是来争论的?因为你似乎忽略了人们所说的话。JavaScript是JSON的超集,它包含了JSON中没有的东西。在这种情况下,你的字符串被解释为其中的其他内容之一,而JSON将其解释为JSON。它是一个有效的对象表达式,但在那个上下文中它不能被解析为一个对象表达式。 - Chuck
我并没有忽略你,我正在努力理解。在此过程中,我一直在回复附加信息。 - Jeff Lowery
显示剩余3条评论

0
如果你真的想要使用eval而不是JSON.parse()来解析JSON,那么你应该编写类似这样的代码:
var o2; // declare o2 out of eval in case of "use strict"
eval("o2 = "+s1); // parse s1 and the assignment to the local o2
console.log(o2); // enjoy the local variable :)

...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接