JavaScript中eval语句浅尝

1.eval的初体验

eval这个语句,怎么说呢?十分好用,其实换句话说“用来装逼真是挺不错的。”
它接收一个字符串的实参,然后就可以像在解释器上输入对应字符串的内容那样执行该字符串。不信?看看这个

// 我们可以用它定义一个函数
> eval("function a(m) {return m + 1}")
undefined
> a(10)
11

// 注意如果是匿名函数要这样
> a = eval("(function(m) {return m + 1})")
[Function]
> a(10)
11

// 进行算数运算
> eval("1+2+3+4")
10

// 它还可以利用当前上下文
> x = 1222222
1222222
> eval("x")
1222222

如果eval里面的值不是字符串的话则返回实参的值!!

> eval(12)
12

// 计算是字符串对象也不行
> eval(new String("console"))
[String: 'console']

这看起来确实很牛逼。有许多动态语言如Python, Ruby都有这个函数。不过我也很少在代码里见到它的身影。

这里传进去的字符串必须是符合语义的,不然的话会报语法错误。

对于这个方法的使用一直都有争议。我看到的下面两种说法。

1. 安全性考虑

eval比较危险。想想这样一种情形,如果我们不小心把eval接口开放了一部分出去。其实就相当于给我们的用户执行代码的权利。用户有意或是无意地让我们eval一些不符合语法规则的字符串内容,有可能会让我们的系统抛出异常。

2.优化不足

现代的JS解释器对大部分JS语法解析都进行了优化,而对那些调用了eval()的函数优化得并不是很多。

其实大多数情况下我们完全可以不必选择eval。目前来讲有很多的可替代方案。

Don't use eval needlessly!

这里我举个例子。

有的时候我们可能想通过迭代来访问一个对象的各种属性,而事先我们不知道对象的属性有哪些是需要的,因此没办法把属性硬编到代码里面。

var dict = {x: 1, y: 2}

// 获取需要访问的键
function getKey() {return 'x'}
var key = getKey();

console.log(eval("dict." + key));

我们如果事先不知道我们要访问的键为 'x'我们可能会写出上面的代码。其实可以用下面的代码来代替。虽然逼格相对没那么高,但是我觉得这样表达更为清晰。

var dict = {x: 1, y: 2}

// 获取需要访问的键
function getKey() {return 'x'}
var key = getKey();

console.log(dict[key]);

除非不得已否则最好还是不要用eval

因为你还有可能还会写出这样的代码。下面这个是我从资料中拿到的。比较有代表性:

function boo(a) {
    eval(a)
};

boo("return;");

这句代码看似没问题,其实它会报这个错误

SyntaxError: Illegal return statement

这是为什么呢?因为eval实际参数的字符串执行时的上下文和调用eval函数的上下文环境是一样的,这使得eval("return;")被理解成单独的语句,而不是放在另一个函数里面执行的语句。(这可能有点难理解)。而return;如果不放在函数里面就没有任何意义了。所以直接报语法错误。

还不能理解?

在看我举个例子,我稍微改变一下原来的脚本

function boo(a) {
    var a = "console.log('mj')";
    eval(a)
};

boo("return;");

这个时候函数就会打印出mj,由于我们赋予变量a另外一个值。而这个字符串"console.log('mg')"执行的上下文,其实就是在boo函数里面。而当a = "return;"的时候我们可以把解释器看成在一个普通的上下文作用域上面单独执行return;语句而没有关注它是否在一个函数里面。然而这是不符合语义的。

下面再来看一个比较过瘾的例子

var x = "global";
// 我在node上跑这里需要设置成全局变量才行不能加var
y = "global";
var geval = eval;

// 函数f中eval执行字符串的上下文跟调用它的上下文作用域是相同的
function f() {
    var x = "local";
    eval("x += 'changed';");
    return x;
}

// 函数g中geval执行的上下文是全局对象作为其上下文变量
function g() {
    var y = "local";
    geval("y += 'changed';");
    return y;
}

console.log(f(), x);
console.log(g(), y);

最后运行的结果是

localchanged global
globalchanged local 

表明了f函数是对函数内部x变量进行了拼接,而g函数却对全局变量y进行了拼接。所以这两个函数调用eval的上下文是不一样的。

2.eval的严格模式

这里也稍微提一下Javascript的严格模式。如果在脚本中开启严格模式(现在很多地方都是这样干的,可以帮助我们写出更加严谨的Javascript代码。)

在正常模式下eval的语法还是相对比较宽松的。
而在严格模式下,主要有这两点限制。

  1. 不能在局部作用域中定义新的变量和函数。但是我们可以修改原来局部变量的值。
  2. 严格模式中eval被认为是保留字。我们不能用别的函数覆盖掉原来的eval函数。不然真的就乱套了。

还是简单举例说明:

function a() {
    var x = 2;
    eval("var y = 1");
    eval("var x = 12");
    console.log(x);
    console.log(y);
}
a();

这里打印的结果我们应该都能想到 121;
我们在严格模式下试试这个脚本。原则上我们只要在脚本的最开始加上'use strict';就能够开启严格模式。


'use strict';
function a() {
    var x = 2;
    eval("var y = 1");
    eval("var x = 12");
    console.log(x);
    console.log(y);
}
a();

这个时候在node下再运行脚本就会报错了。

ReferenceError: y is not defined

这里我们用eval创建局部变量y失败了,但是我们可以修改已有的局部变量x

啊,绕得好累,还有最后一个例子很简单,我们试着在严格模式下覆盖eval的值。

'use strict'
eval = function() {};

解释器直接报错

SyntaxError: Unexpected eval or arguments in strict mode

我们不能在严格模式下做这种事情。严格模式有助于我们写出解释器比较认可的代码, 或者我们可以理解为Javascript的未来。有些东西它会直接用报错来提醒我们。比如当我们想通过delete来删除局部变量的时候,之前说过它会直接返回结果false,但是如果在严格模式下,这种行为解释器会直接抛出异常。有些大牛还是建议我们写脚本的时候尽可能用严格模式来写,有助于我们写出专业点的Javascript代码,也有助于日后的维护升级。

Happy Coding!!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355

推荐阅读更多精彩内容