5

一、简介

eval和new Function都可以将一段字符串解析成一段JS脚本并执行

二、eval和 new Function的使用

① eval:

eval作为一个方法,直接传入要解析的字符串即可

const foo = "foo"; // 定义了一个变量foo
const str = "console.log(foo)";
eval(str); // 解析str字符串中包含JS脚本并执行,输出foo

② new Function:
JS中的每个函数都是 Function 类型的实例,即JS中所有的函数的proto都指向Function的prototype,或者说,JS中所有的函数都是由Function构造出来的

new Function() 可以接n个参数,最后一个参数作为函数体

let func = new Function ([arg1[, arg2[, ...argN]],] functionBody);
let foo = new Function("name", "console.log(name)");
// 创建的函数指向Function的原型
console.log(foo.__proto__ === Function.prototype); // true

// 等价于
function foo(name) {
    console.log(name);
}

可以看到Function的最后一个参数字符串"console.log(name)"也被解析成了JS脚本,并且作为函数体执行了

需要注意的是,传入的字符串不是任意的,必须是能当做JS脚本正常执行的字符串

const str = "{'foo': '123'}";
const obj = eval(str);
console.log(obj);
// 报错信息如下
VM1061:1 Uncaught SyntaxError: Unexpected token ':'
    at <anonymous>:2:18

因为传入"{'foo': foo}"的时候,{}会被解析为代码块,相当于直接执行const str = "'foo': foo";解决方法可以在外层加上一个括号,如:

const str = "({'foo': '123'})";
const obj = eval(str);
console.log(obj);// {foo: '123'}

三、eval和new Function的区别

eval中的代码执行时的作用域为当前作用域它可以访问到函数中的局部变量
new Function中的代码执行时的作用域为全局作用域不论它的在哪个地方调用的它访问的都是全局变量

let foo = "foo";
function bar() {
    let foo = "bar";
    eval("console.log(foo)"); // 输出bar
    new Function("console.log(foo)")(); // 输出foo
}
bar();

从输出结果可以看出,new Function访问到的始终是全局作用域中的变量;eval访问到的则是当前作用域中的变量
当然如果使用的是window.eval()或者global.eval()那么访问到的也始终是全局作用域中的变量

let foo = "foo";
function bar() {
    let foo = "bar";
    window.eval("console.log(foo)"); // 输出foo
    new Function("console.log(foo)")(); // 输出foo
}
bar();

此时输出都是foo了
如果全局作用域中没有指定的变量,那么将会报错,提示该变量没有定义

function bar() {
    let foo = "bar";
    window.eval("console.log(foo)"); // 报错
    new Function("console.log(foo)")(); // 报错
}
bar();
// 报错信息如下
VM1051:1 Uncaught ReferenceError: foo is not defined
    at eval (eval at bar ((index):3), <anonymous>:1:13)
    at eval (<anonymous>)
    at bar (<anonymous>:3:12)
    at <anonymous>:6:1

四、使用场景

在Vue生成渲染函数的时候会使用到,Vue中进行模板编译的时候,最终会将模板编译成一段可执行JS字符串,然后传递给new Function生成渲染函数,渲染(挂载)的时候,执行这个渲染函数拿到对应的虚拟DOM节点,如:

// template模板
<div id="app" style="color: red;background: blue;"><p>hello {{name}}</p>{{msg}}</div>
// 解析模板生成一段字符串,即渲染函数要执行的字符串
let code = _c("div", {id: "app",style: {"color":" red","background":" blue"}},_c("p", undefined,_v("hello"+_s(name))),_v(_s(msg)))
// 将渲染函数要执行的字符串传入new Function()生成渲染函数
let renderFn = new Function(`with(this) {return ${code}}`);

等到渲染的时候,就会执行渲染函数,即renderFn(),此时就会执行code所指的字符串,拿到对应的虚拟DOM节点,进行patch最终更新真实的DOM节点。


JS_Even_JS
2.6k 声望3.7k 粉丝

前端工程师