From 75142d4f632d7bffe74cc50724d32f51f6dfd676 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Wed, 24 Jan 2018 14:19:26 +0800 Subject: [PATCH] docs(types): edit function/eval --- docs/types/function.md | 62 +++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/docs/types/function.md b/docs/types/function.md index 5a5e15c..61cdcdb 100644 --- a/docs/types/function.md +++ b/docs/types/function.md @@ -893,7 +893,9 @@ storeData(tmp); ## eval 命令 -`eval`命令的作用是,将字符串当作语句执行。 +### 基本用法 + +`eval`命令接受一个字符串作为参数,并将这个字符串当作语句执行。 ```javascript eval('var a = 1;'); @@ -902,10 +904,24 @@ a // 1 上面代码将字符串当作语句运行,生成了变量`a`。 +如果参数字符串无法当作语句运行,那么就会报错。 + +```javascript +eval('3x') // Uncaught SyntaxError: Invalid or unexpected token +``` + 放在`eval`中的字符串,应该有独自存在的意义,不能用来与`eval`以外的命令配合使用。举例来说,下面的代码将会报错。 ```javascript -eval('return;'); +eval('return;'); // Uncaught SyntaxError: Illegal return statement +``` + +上面代码会报错,因为`return`不能单独使用,必须在函数中使用。 + +如果`eval`的参数不是字符串,那么会原样返回。 + +```javascript +eval(123) // 123 ``` `eval`没有自己的作用域,都在当前作用域内执行,因此可能会修改当前作用域的变量的值,造成安全问题。 @@ -944,11 +960,21 @@ a // 2 上面代码中,严格模式下,`eval`内部还是改写了外部变量,可见安全风险依然存在。 -此外,`eval`的命令字符串不会得到 JavaScript 引擎的优化,运行速度较慢。这也是一个不应该使用它的理由。 +总之,`eval`的本质是在当前作用域之中,注入代码。由于安全风险和不利于 JavaScript 引擎优化执行速度,所以一般不推荐使用。通常情况下,`eval`最常见的场合是解析 JSON 数据的字符串,不过正确的做法应该是使用原生的`JSON.parse`方法。 -通常情况下,`eval`最常见的场合是解析 JSON 数据字符串,不过正确的做法应该是使用浏览器提供的`JSON.parse`方法。 +### eval 的别名调用 -JavaScript 引擎内部,`eval`实际上是一个引用,默认调用一个内部方法。这使得`eval`的使用分成两种情况,一种是像上面这样的调用`eval(expression)`,这叫做“直接使用”,这种情况下`eval`的作用域就是当前作用域。除此之外的调用方法,都叫“间接调用”,此时`eval`的作用域总是全局作用域。 +前面说过`eval`不利于引擎优化执行速度。更麻烦的是,还有下面这种情况,引擎在静态代码分析的阶段,根本无法分辨执行的是`eval`。 + +```javascript +var m = eval; +m('var x = 1'); +x // 1 +``` + +上面代码中,变量`m`是`eval`的别名。静态代码分析阶段,引擎分辨不出`m('var x = 1')`执行的是`eval`命令。 + +为了保证`eval`的别名不影响代码优化,JavaScript 的标准规定,凡是使用别名执行`eval`,`eval`内部一律是全局作用域。 ```javascript var a = 1; @@ -962,9 +988,9 @@ function f() { f() // 1 ``` -上面代码中,`eval`是间接调用,所以即使它是在函数中,它的作用域还是全局作用域,因此输出的`a`为全局变量。 +上面代码中,`eval`是别名调用,所以即使它是在函数中,它的作用域还是全局作用域,因此输出的`a`为全局变量。这样的话,引擎就能确认`e()`不会对当前的函数作用域产生影响,优化的时候就可以把这一行排除掉。 -`eval`的间接调用的形式五花八门,只要不是直接调用,都属于间接调用。 +`eval`的别名调用的形式五花八门,只要不是直接调用,都属于别名调用,因为引擎只能分辨`eval()`这一种形式是直接调用。 ```javascript eval.call(null, '...') @@ -973,27 +999,7 @@ window.eval('...') (eval, eval)('...') ``` -上面这些形式都是`eval`的间接调用,因此它们的作用域都是全局作用域。 - -与`eval`作用类似的还有`Function`构造函数。利用它生成一个函数,然后调用该函数,也能将字符串当作命令执行。 - -```javascript -var jsonp = 'foo({"id": 42})'; - -var f = new Function( 'foo', jsonp ); -// 相当于定义了如下函数 -// function f(foo) { -// foo({"id":42}); -// } - -f(function (json) { - console.log( json.id ); // 42 -}) -``` - -上面代码中,`jsonp`是一个字符串,`Function`构造函数将这个字符串,变成了函数体。调用该函数的时候,`jsonp`就会执行。这种写法的实质是将代码放到函数作用域执行,避免对全局作用域造成影响。 - -不过,`new Function()`的写法也可以读写全局作用域,所以也是应该避免使用它。 +上面这些形式都是`eval`的别名调用,作用域都是全局作用域。 ## 参考链接