eval
与模板字符串的一个不同之处在于,模板字符串在编译时解析,而eval
的参数只有在运行时执行eval
时才会被解析。
与此相关的是,eval
可以获得一个动态构建的参数,而模板字面量是文字:它不能作为模板变量存储,也不能在动态构建、移动和最终解析。不存在“模板变量”数据类型。标签函数实际上不是获得一个模板变量作为参数,而是它所包含的已知于编译时的解析组件。
一些例子
使用eval
,您可以得到以下情况:
var code = prompt('enter some evil code');
eval(code);
但是使用模板字面量无法实现此操作:
var literal = prompt('enter some evil template literal');
tag literal; // there is no data type or syntax for this.
`${literal}`; // and this just gives you the entered string.
这是可能的:
var str = prompt('enter some string');
tag`${str}`;
但这并不会导致意外的代码执行,至少不会比这更糟:
var str = prompt('enter some string');
myfunc(str);
任何函数调用必须已经被直接编码到模板文字中。字符串变量的值无法改变这一点。不能通过模板文字调用变量函数。
`${func(str)}`;
......将调用 func
函数,而且只有这个函数。这是由程序员选择的。
一个相当恶劣的模板文字
话虽如此,这仍然是可能的:
var func = prompt ("enter some evil function name (suggestion: 'alert')");
var param = prompt ("now provide an argument for " + func);
`${window[func](param)}`;
但很明显该程序自愿地打开了在全局对象上执行任何函数的可能性。这样,您确实接近于eval
的邪恶。
请注意,同样的效果可以通过以下方式实现:
window[name](param)
最邪恶的模板字面量
正如评论所述,你可以使用以下模板字面量:
`eval(str)`;
......所以邪恶的部分不在于模板字面量,而在于您设计的通用函数调用。对此,您不需要使用模板字面量或 eval
,而是一个糟糕的程序员;-)
关于这个例子
您举了这个例子:
let ii = 1;
function counter() {
return ii++;
}
console.log(`${counter()}, ${ii++}, ${counter()}`);
这会调用你的counter
函数,但与eval
的区别在于字符串字面量已经在设计时存在,不能在运行时构造。这段代码旨在递增您的计数器,并且与以下代码本质上没有区别:
console.log(counter() + ', ' + (ii++) + ', ' + counter());
编译时间
为了强调编译时和运行时解析的差异,请注意不能使用语法无效的模板字面量来运行代码。
比较这两个脚本:
alert('press OK');
eval('alert("hello)');
并且:
alert('press OK');
`${alert("hello)}`;
注意语法错误。第一个脚本只有在解析eval
的参数时运行时才会注意到语法错误,而第二个脚本甚至不会运行并立即给出语法错误。
更确切地说,eval
执行一个新的脚本,具有自己的编译和运行阶段。模板字面量与其他代码一样被解析/编译。
eval()
严格得多,因为它们通常只评估表达式并将结果强制转换为字符串。 - Pointyeval
函数可以执行任意字符串作为代码。我认为你不能用模板文字来实现这个功能,因为它们是完全不同的东西。 - Orioleval
更安全... - Leonid Beschastnyconsole.log(counter() + ', ' + ii++ + ', ' + counter())
进行比较,这是完全等效的。您仍然认为它不安全吗? - Felix Kling