mirror of
https://github.com/wangdoc/javascript-tutorial.git
synced 2025-12-24 11:14:21 +08:00
docs(types): edit function/eval
This commit is contained in:
@@ -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`的别名调用,作用域都是全局作用域。
|
||||
|
||||
## 参考链接
|
||||
|
||||
|
||||
Reference in New Issue
Block a user