万恶的 eval() ?

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 万恶的 eval() ?

什么是eval()?

eval()函数就是一个完整的 ECMAScript 解释器,它接收一个参数,即一个要执行的 ECMAScript(JavaScript)字符串。


与很多解释型语言一样,JavaScript有能力解释JavaScript源代码字符串,对它们求值以产生一个值。JavaScript就是通过全局函数eval()来对源代码字符串求值的。


关于eval()是函数还是操作符?

eval()是一个函数,其实应该是个操作符。JavaScript语言最初的版本定义了一个eval()函数,语言设计者和解释器开发者一直对它加以限制,导致它越来越像操作符。


现代JavaScript解释器会执行大量代码分析和优化。一般来说,如果一个函数调用eval(),则解释器将无法再优化该函数。


把eval()定义为函数的问题在于可以给它起不同的名字如果可以这样,那么解释器无法确定哪个函数会调用eval(),也就无法激进优化。假如eval()是个操作符(即保留字),那这个问题就可以避免。


作用域问题

通过 eval()执行的代码属于该调用所在上下文,被执行的代码与该上下文拥有相同的作用域链。


在执行 eval(..) 之后的代码时,引擎并不“知道”或“在意”前面的代码是以动态形式插入进来,并对词法作用域的环境进行修改的。引擎只会如往常地进行词法作用域查找。


eval()的使用

关于参数

eval()期待一个参数。

1.如果给它传入任何非字符串值,它会简单地返回这个值。

2.如果传入字符串,它会尝试把这个字符串当成JavaScript代码来解析,

①解析失败会抛出SyntaxError。

②如果最后一个表达式或语句没有值则返回undefined。

③如果解析字符串成功,它会求值代码并返回该字符串中最后一个表达式或语句的值。

④如果求值字符串抛出异常,该异常会从调用eval()的地方传播出来。


直接eval()

对于eval()(在像这样调用时),关键在于它会使用调用它的代码的变量环境。它会像本地代码一样查找变量的值、定义新变量和函数。

  1. 定义在包含上下文中的变量可以在 eval()调用内部被引用

如果一个函数定义了一个局部变量x,然后调用了eval("x"),那它会取得这个局部变量的值。

function foo() {
    let x = 1 + 1;
    eval("console.log(x)"); // 2
}
foo()
复制代码


  1. 声明局部变量

如果这个函数调用了eval("var y = 3;"),则会声明一个新局部变量y。

eval("var y = '3';"); 
console.log(y); // 3
复制代码


eval()声明一个局部函数

eval(`function foo() {
    let x = 1 + 1;
    console.log(x);
}`)
foo()
复制代码


  1. 如果被求值的字符串使用了letconst,则声明的变量或常量会被限制在求值的局部作用域内,不会定义到调用环境中。
    通过 eval()定义的任何变量和函数都不会被提升,这是因为在解析代码的时候,它们是被包含在一个字符串中的。它们只是在 eval()执行的时候才会被创建。
eval("var msg = 'hello world';"); 
console.log(msg); // hello world
eval("let msg = 'hello world';"); 
console.log(msg); // Reference Error: msg is not defined
复制代码


  1. 传给eval()的代码字符串本身必须从语法上说得通:不能使用它向函数中粘贴代码片段。

eval("return;")是没有意义的,因为return只在函数中是合法的,即使被求值的字符串使用与调用函数相同的变量环境,这个字符串也不会成为函数的一部分。

只要这个字符串本身可以作为独立的脚本运行(即使像x=0这么短),都可以合法地传给eval()。否则,eval()将抛出SyntaxError。


严格模式

一、严格模式对eval()函数增加了更多限制,甚至对标识符“eval”的使用也进行了限制。


当我们在严格模式下调用eval()时,或者当被求值的代码字符串以“use strict”指令开头时,eval(..) 在运行时有其自己的词法作用域,eval()会基于一个私有变量环境进行局部求值。这意味着在严格模式下,被求值的代码可以查询和设置局部变量,但不能在局部作用域中定义新变量或函数也就是说在 eval()内部创建的变量和函数无法被外部访问。


function foo(str) {
    "use strict";
    eval(str);
    console.log(a); // ReferenceError: a is not defined
}
foo("var a = 2");
复制代码


二、严格模式让eval()变得更像操作符,因为“eval”在严格模式下会变成保留字。

不能再使用新值来重写eval()函数。通过名字“eval”来声明变量、函数、函数参数或捕获块参数都是不允许的。


全局eval()

一、

eval()会干扰JavaScript的优化程序,是因为它能够修改局部变量。解释器也不会过多优化调用eval()的函数。

JavaScript规范中说,如果eval()被以“eval”之外的其他名字调用时,它应该把字符串当成顶级全局代码来求值。

被求值的代码可能定义新全局变量或全局函数,可能修改全局变量,但它不会再使用或修改调用函数的局部变量。因此也就不会妨碍局部优化。

使用名字“eval”来调用eval()函数就叫作“直接eval”(这样就有点保留字的感觉了)。直接调用eval()使用的是调用上下文的变量环境。如果在顶级代码中调用eval(),则它操作的一定是全局变量和全局函数。


二、

任何其他调用方式,包括间接调用,都使用全局对象作为变量环境,因而不能读、写或定义局部变量或函数(无论直接调用还是间接调用都只能通过var来定义新变量。在被求值的字符串中使用let和const创建的变量和常量会被限定在求值的局部作用域内,不会修改调用或全局环境)。

let x = 'g';
let y = 'g';
let gEval = eval;
function g() {
    let x = 'l';
    gEval("x += 'changed'");
    return x;
}
function l() {
    let y = 'l';
    eval("y += 'changed'");
    return y;
}
console.log(g() , x); // l gchanged  g函数外部x改变了
console.log(l() , y); // lchanged g  l函数内部y改变了
复制代码


这种全局求值的能力不仅仅是为了适应优化程序的需求,同时也是一种极其有用的特性,可以让我们把代码字符串作为独立、顶级的脚本来执行。假如你必须使用eval(),那很可能应该使用它的全局求值而不是局部求值。


为什么不提倡使用eval()?

解释代码字符串的能力是非常强大的,但也非常危险。在使用 eval()的时候必须极为慎重,特别是在解释用户输入的内容时。因为这个方法会对 XSS 利用暴露出很大的攻击面。恶意用户可能插入会导致你网站或应用崩溃的代码。


某些Web服务器使用HTTP的"Content-Security-Policy"头部对整个网站禁用eval()

无论在什么时候eval()都可以运行期间修改书写期的词法作用域。除了严格模式。


和eavl函数有类似作用

setTimeout(..) 和 setInterval(..) 的第一个参数可以是字符串,字符串的内容可以被解释为一段动态生成的函数代码。这些功能已经过时且并不被提倡。不要使用它们!


new Function(..) 函数的行为也很类似,最后一个参数可以接受代码字符串,并将其转化为动态生成的函数(前面的参数是这个新生成的函数的形参)。这种构建函数的语法比 eval(..) 略微安全一些,但也要尽量避免使用。


在程序中动态生成代码的使用场景非常罕见,因为它所带来的好处无法抵消性能上的损失。



目录
相关文章
|
5月前
|
程序员 Python
[oeasy]python0028_女性程序员_Eniac_girls_bug_Grace
回顾上次内容,我们了解到 `.py` 文件中的代码是按顺序一行行被解释执行的,可以使用 `pdb3 hello.py` 来调试程序。此外,我们探讨了“bug”这一术语的由来,它最早是在 1947 年由 Grace Murray Hopper 发现的一只真正的飞蛾所引起的计算机故障,从此“debugging”成了查找并修复程序错误的过程。早期的程序员大多为女性,因为她们通常更加细心且有耐心,这些特质对于检查错综复杂的线路和编程工作至关重要。编程与编织有着相似之处,都需要细致和有条理的操作。最后,我们认识到 bug 的存在是程序员工作的基础,没有 bug 就不需要程序员去修正它们。
49 3
|
安全 测试技术 Windows
利用Pascal+zutto_dekiru进行免杀
利用Pascal+zutto_dekiru进行免杀
86 0
|
8月前
|
安全 Python
使用eval函数需要注意哪些方面
使用eval函数需要注意哪些方面
72 0
xprintlog:给print函数加点料
xprintlog:给print函数加点料
102 0
|
机器学习/深度学习 人工智能 BI
Educational Codeforces Round 115 (Rated for Div. 2) D. Training Session(组合数学 思维)
Educational Codeforces Round 115 (Rated for Div. 2) D. Training Session(组合数学 思维)
116 0
|
安全 前端开发 测试技术
经理看到我用eval,过来就是一jio
背景: 前端根据用户输入的内容(输入一个数据),视图层自动给解析并求值成 字符串/整形/字典/浮点型/列表等。
经理看到我用eval,过来就是一jio
|
并行计算 TensorFlow 算法框架/工具
逐步解决安装Keras后运行程序出现的问题哭晕在厕所里-‘transpose_shape‘ from ‘keras.utils.generic_utils‘
逐步解决安装Keras后运行程序出现的问题哭晕在厕所里-‘transpose_shape‘ from ‘keras.utils.generic_utils‘
逐步解决安装Keras后运行程序出现的问题哭晕在厕所里-‘transpose_shape‘ from ‘keras.utils.generic_utils‘
HDOJ 1339 A Simple Task(简单数学题,暴力)
HDOJ 1339 A Simple Task(简单数学题,暴力)
123 0
|
Shell Python
初学Python之eval函数的嵌套eval中的eval
今天老师布置了一个作业,很简单的几行代码。但是也算让我彻底知道了Python中的eval这个函数了,因为之前学过一点Python,以为自己已经知道了eval函数的用法(还以为这是用来自动识别输入类型,可以用来输入字典) 结果啪啪打脸了。呜呜呜。。。
239 0
大声说出你对女神的爱!Geek is A choice. Girls make difference.
女王节来了,我们采访了来自于阿里云智能一线的6位geek girl,用两天的时间近距离观察她们快乐工作的,还在银泰百货的支持下绽放她们认真生(chou)活(mei)的光芒。 雏恬 我不想做被保护的女生,我想做改变世界的极客。