一个区别eval
是模板文字是在编译时解析的,而参数 toeval
只在运行时被解析,当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
执行一个新脚本,具有自己的编译和运行阶段。模板文字像其他代码一样被解析/编译。