在JavaScript中eval() 和 new Function() 的执行上下文差异及安全防护

前端小伙伴们,有没有被动态执行代码搞到头疼过?用eval写了段用户输入的代码,结果页面突然弹广告;想用new Function封装动态逻辑,发现变量怎么都访问不到……今天咱们就聊聊JavaScript里的"动态执行双兄弟"——eval()new Function(),用最接地气的话讲清它们的核心区别,看完这篇,你不仅能避开坑,还能和面试官唠明白背后的逻辑~

一、动态执行代码的"翻车现场"

先讲个我上周踩的坑:给客户做表单验证工具,需要根据用户输入的规则动态执行校验逻辑。用户提交了一段规则:

"return username === 'admin' && password === '123456'"

我用eval直接执行这段代码,结果用户偷偷加了句document.write('<script src="恶意链接"></script>'),页面瞬间被篡改……后来改用new Function,发现怎么都访问不到外层的username变量,差点被客户骂哭~

类似的场景还有:配置化系统的动态表达式计算、低代码平台的逻辑执行……这些问题的根源,都和evalnew Function执行上下文差异安全风险有关。

二、从执行上下文看本质区别

要搞懂两者的区别,得先明白JavaScript的执行上下文(Execution Context)——它是代码运行时的"环境",包含变量对象、作用域链、this等信息。evalnew Function的核心差异,就在于它们如何创建和使用这个"环境"。

1. 执行上下文的创建规则

eval:在当前执行上下文中执行代码。它会"潜入"当前作用域,直接访问和修改外层的变量(包括函数作用域、块级作用域)。

官方文档说它是:“Evaluates JavaScript code represented as a string in the current scope.”(在当前作用域中计算字符串形式的JavaScript代码。)

new Function:创建一个新的函数执行上下文,其作用域链仅包含全局作用域(或模块作用域)。它像一个"隔离舱",无法直接访问外层函数的局部变量(除非通过闭包或全局变量)。

2. 作用域链的差异

eval的作用域链:与调用它的函数共享作用域链。比如在函数fn()里调用evaleval可以访问fn的局部变量、外层函数的变量,甚至全局变量。
示例:

function demo() {
  const localVar = "我是局部变量";
  eval("console.log(localVar)"); // 可以访问localVar,输出:我是局部变量
}
demo();

new Function的作用域链仅包含全局作用域(或模块作用域)。即使在函数fn()里创建new Function,它也无法访问fn的局部变量(除非变量是全局的)。
示例:

function demo() {
  const localVar = "我是局部变量";
  const func = new Function("console.log(localVar)"); // 报错:localVar未定义
  func();
}
demo();

3. this的指向差异

eval中的this:与调用eval时的this一致。比如在全局调用evalthiswindow;在对象方法中调用evalthis是对象实例。
示例:

const obj = {
  name: "对象",
  run() {
    eval("console.log(this.name)"); // this指向obj
  }
};
obj.run(); // 输出:对象

new Function中的this:在非严格模式下指向全局对象(window/global);严格模式下是undefined。与创建它的作用域的this无关。
示例:

const obj = {
  name: "对象",
  run() {
    const func = new Function("console.log(this.name)"); // this是window(name是undefined)
    func();
  }
};
obj.run(); // 输出:undefined

三、代码示例:对比出真知

示例1:访问外层变量

// 测试函数:包含局部变量
function testScope() {
  const a = 10;
  const b = 20;

  // 用eval访问局部变量
  eval("console.log('eval访问a:', a)"); // 输出:eval访问a: 10

  // 用new Function访问局部变量(失败)
  const func = new Function("console.log('new Function访问a:', a)"); 
  try {
    func();
  } catch (e) {
    console.log("new Function报错:", e.message); // 输出:new Function报错: a is not defined
  }

  // 用new Function访问全局变量(成功)
  window.c = 30; // 全局变量c
  const func2 = new Function("console.log('new Function访问c:', c)");
  func2(); // 输出:new Function访问c: 30
}
testScope();

示例2:修改外层变量

function testModify() {
  let x = 1;

  // eval修改外层变量(成功)
  eval("x = x + 1;");
  console.log("eval修改后x:", x); // 输出:eval修改后x: 2

  // new Function修改外层变量(失败,只能修改全局变量)
  const func = new Function("x = x + 1;");
  func(); 
  console.log("new Function修改后x:", x); // 输出:new Function修改后x: 2(x还是2?因为func修改的是全局x)
  console.log("全局x:", window.x); // 输出:全局x: NaN(因为全局x未定义,x+1是NaN)
}
testModify();

示例3:this指向对比

// 全局作用域调用
eval("console.log('eval全局this:', this === window)"); // 输出:true

const func1 = new Function("console.log('new Function全局this:', this === window)");
func1(); // 输出:true(非严格模式)

// 对象方法中调用
const obj = {
  name: "测试对象",
  evalTest() {
    eval("console.log('eval对象this:', this.name)"); // 输出:测试对象
  },
  funcTest() {
    const func = new Function("console.log('new Function对象this:', this.name)");
    func(); // 输出:undefined(this是window,无name属性)
  }
};
obj.evalTest();
obj.funcTest();

四、一张表总结核心差异

对比项eval()new Function()
执行上下文当前作用域(与调用者共享)新的函数作用域(仅全局作用域)
访问外层变量可以(直接访问函数/块级作用域)不可以(仅能访问全局变量)
修改外层变量可以(直接修改函数/块级作用域变量)不可以(只能修改全局变量)
this指向与调用者的this一致非严格模式:全局对象;严格模式:undefined
安全风险极高(可访问所有作用域变量)较高(但无法访问局部变量)
性能较差(破坏引擎优化)较差(每次创建新函数)
适用场景极少数(如必须操作当前作用域)动态创建函数(需隔离作用域)

五、面试题回答方法

正常回答(结构化):

eval()new Function()的执行上下文差异及安全防护主要体现在:

1、执行上下文eval在当前作用域执行,可访问和修改外层变量;new Function在新的全局作用域执行,无法访问局部变量。

2、this指向evalthis与调用者一致;new Functionthis是全局对象(非严格模式)。

3、安全风险eval风险更高(可访问所有作用域),易导致XSS攻击;new Function风险较低(仅能访问全局)。

4、防护措施:避免使用、严格校验输入、使用沙盒、替换为Function.prototype.bind等安全方案。”

大白话回答(接地气):

eval就像‘拆家小能手’——它直接钻进你家(当前作用域),能翻你抽屉(局部变量)、改你存折(修改变量)。比如你在函数里用eval,它能随便动你函数里的变量。

new Function像‘隔离房住户’——它住在独立的小房间(全局作用域),看不到你家抽屉(局部变量),只能翻小区公共信箱(全局变量)。

安全方面,eval太危险,给它段恶意代码,它能拆你整个家(XSS攻击);new Function虽然安全点,但也别随便放陌生人进去(用户输入)。”

六、总结:3个使用原则+4层安全防护

3个使用原则:

1)能不用就不用:现代JS的Proxy、模板字符串、动态导入等方案,基本能替代动态执行需求;

2)优先new Function:需动态执行代码时,new Functioneval更安全(无法访问局部变量);

3)严格校验输入:必须使用时,对输入的代码字符串做严格校验(如正则匹配允许的关键字)。

4层安全防护:

1)输入白名单校验:只允许特定关键字(如return、数学运算符),禁止documentwindow等危险对象。
示例(校验函数):

function validateCode(code) {
  const allowedKeywords = /^[\w\s\+\-\*\/\(\)\.]+\;?$/; // 允许数字、字母、运算符等
  return allowedKeywords.test(code);
}

2)使用strict mode:在evalnew Function中启用严格模式,限制thisundefined,禁止with等危险语法。
示例:

eval("'use strict'; console.log(this)"); // 输出:undefined
const func = new Function("'use strict'; console.log(this)");
func(); // 输出:undefined

3)沙盒环境:用iframeWorker创建隔离的沙盒,限制代码的访问权限(如禁止访问DOM)。
示例(iframe沙盒):

<iframe id="sandbox" sandbox="allow-scripts"></iframe>
<script>
  const iframe = document.getElementById('sandbox');
  const code = "console.log('沙盒内执行:', document.title)"; // 无法访问外层document
  iframe.contentWindow.eval(code);
</script>

4)替代方案:用Function.prototype.bind、模板字符串等安全方式实现动态逻辑。
示例(模板字符串替代):

const username = "admin";
const password = "123456";
const rule = "username === 'admin' && password === '123456'";
const result = eval(rule); // 危险!改用:
const result = new Function("username", "password", `return ${rule}`)(username, password);

七、扩展思考:4个高频问题解答

问题1:evalnew Function的性能哪个更差?

解答:两者性能都较差,但eval更糟。因为eval会破坏JavaScript引擎的优化(如JIT编译),导致代码无法被高效执行。new Function每次创建新函数,也会有一定开销,但比eval略好。

问题2:eval可以访问块级作用域(let/const)吗?

解答:可以!eval在严格模式下可以访问块级作用域的变量。
示例:

function testBlockScope() {
  {
    const blockVar = "块级变量";
    eval("'use strict'; console.log(blockVar)"); // 输出:块级变量(严格模式)
  }
}
testBlockScope();

问题3:new Function可以访问闭包变量吗?

解答:不能直接访问,但可以通过参数传递闭包变量。
示例:

function createFunc() {
  const closureVar = "闭包变量";
  // 通过参数传递闭包变量
  return new Function("param", "console.log(param)"); 
}
const func = createFunc();
func(closureVar); // 输出:闭包变量(需要手动传递)

问题4:现代框架(如React/Vue)禁止使用eval吗?

解答:框架本身不禁止,但打包工具(如Webpack)会将eval标记为危险操作,部分安全策略(CSP)也会禁止eval。例如,React的dangerouslySetInnerHTML就不推荐与eval配合使用。

结尾:动态执行,安全第一

evalnew Function就像两把锋利的刀——用好了能解决复杂问题,用不好会割伤自己。记住:

  • 能不用就不用,优先选择安全的替代方案;
  • 必须用时,做好输入校验和沙盒隔离;

转自:在JavaScript中eval() 和 new Function() 的执行上下文差异及安全防护?_new function的执行上下文-CSDN博客 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值