深入了解eval

本博客 hjy-xh,转载请申明出处

相关定义和描述

eval 是全局对象上的一个函数,会把传入的字符串当做 JavaScript 代码执行,它通常被用来执行动态创建的代码。

语法很简单:

1
>eval(string) // 一个表示 JavaScript 表达式、语句或一系列语句的字符串。表达式可以包含变量与已存在对象的属性。

举个🌰

1
2
const string = "console.log('hello, eval')";
window.eval(string);

先提出问题:eval这个方法很强大,且兼容性很好,但是为什么很少使用?

一些特性

返回最后一个表达式的值

1
console.log(eval('1+1+1')); // 3
1
2
3
4
5
6
7
8
9
10
let string1 = 'let x; x = 1;';
let string2 = 'let x, y; x = 1; y = 2;';

function test(str) {
let result = eval(str);
console.log(result);
}

test(string1); // 1
test(string2); // 2

函数作为字符串被定义时需要”(“和”)“作为前缀和后缀

1
2
3
4
const funcStr1 = 'function test() {}';
const funcStr2 = '(function test() {})';
console.log(eval(funcStr1)); // undefined
console.log(eval(funcStr2)); // [Function: test]

直接调用和间接调用

1
2
3
4
5
6
7
8
9
10
let x = 1, y = 1;
function test() {
let x = 2, y = 2;
console.log(eval('x + y')); // 直接调用,使用本地作用域,结果是 4
const geval = eval; // 等价于在全局作用域调用
console.log(geval('x + y')); // 间接调用,使用全局作用域,结果是 2
console.log((0, eval)('this')); // 另一个间接调用的例子
}

test();
1
(0, eval)('this')

逗号操作符:对它的每个操作数求值(从左到右),并返回最后一个操作数的值

这里使用逗号操作符,于是返回表达式中的最后一项,然后为eval传入’this’字符串,来立即执行这个表达式,这里其实就是把全局对象给打印出来

黑魔法

欺骗词法作用域

原理:JavaScript中的eval(str)函数可以接受一个字符串为参数,并将字符串内容视为好像在书写时就存在于eval()函数所在位置的代码。

1
2
3
4
5
6
7
8
function foo(str, a) {
eval(str); // 欺骗
console.log(a, b);
}

var b = 2;

foo("var b = 3;", 1); // 1, 3

这个例子中var b = 3;这条语句会被当做本来就在那里,因此foo函数内部的变量b遮蔽了外部的变量b

严格模式下的表现

在严格模式下,eval在运行时会有自己的词法作用域,意味着其中的声明无法修改所在的作用域

1
2
3
4
5
6
7
function foo(str, a) {
"use strict"
eval(str);
console.log(a); // undefined
}

foo("var a = 2;");

使用时有哪些坑

eval不容易调试

调试困难,且可读性非常差(没有行号)

用chromeDev、VSCode等工具无法打断点调试

性能问题

JavaScript 通常被认为是一门解释型的语言,但是现代的 JavaScript 引擎不再只是解释 JavaScript,也会对其进行编译。

V8 为了提高 JS的运行性能,在运行之前会先将JS编译为本地的机器码,然后再去执行机器码(JIT)。

现代JavaScript解释器将javascript转换为机器代码。 这意味着任何变量命名的概念都会被删除。 因此,任意一个eval的使用都会强制浏览器进行冗长的变量名称查找,以确定变量在机器代码中的位置并设置其值。 另外,新内容将会通过 eval() 引进给变量, 比如更改该变量的类型,因此会强制浏览器重新执行所有已经生成的机器代码以进行补偿。

eval破坏了JS引擎优化

安全问题

当使用来源不可靠的第三方代码时,无法保证不碰到恶意代码

参考

MDN

JavaScript深入之词法作用域和动态作用域

V8 JavaScript 引擎

《你不知道的JavaScript》 上卷