前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >动态执行脚本

动态执行脚本

作者头像
奋飛
发布2020-05-28 17:06:52
3.3K0
发布2020-05-28 17:06:52
举报
文章被收录于专栏:Super 前端Super 前端

提到动态执行脚本,大家想到的肯定是 evalnew Function(),在 nodejs 中有专属的 vm 模块,可以完成相应的 sandbox 作用。

浏览器中动态执行脚本

eval()

函数会将传入的字符串当做 JavaScript 代码进行执行,返回字符串中代码的返回值;如果参数不是字符串将原封不动返回。

如果你间接的使用 eval(),比如通过一个引用来调用它,而不是直接的调用 eval。从 ECMAScript 5 起,它工作在全局作用域下,而不是局部作用域中。

代码语言:javascript
复制
function test() {
  let x = 2, y = 4;
  console.log(eval('x + y'));  // 直接调用,使用本地作用域,结果是 6
  let geval = eval; // 等价于在全局作用域调用
  console.log(geval('x + y')); // 间接调用,使用全局作用域,throws ReferenceError 因为`x`未定义
  (0, eval)('x + y'); // 另一个间接调用的例子
}

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

代码语言:javascript
复制
let fctStr1 = 'function a() {}'
let fctStr2 = '(function a() {})'
let fct1 = eval(fctStr1)  // 返回undefined
let fct2 = eval(fctStr2)  // 返回一个函数

MDN 建议永远不要使用 eval

  • eval() 使用与调用者相同的权限执行代码。如果你用 eval() 运行的字符串代码被恶意方(不怀好意的人)修改,您最终可能会在您的网页/扩展程序的权限下,在用户计算机上运行恶意代码。更重要的是,第三方代码可以看到某一个 eval() 被调用时的作用域,这也有可能导致一些不同方式的攻击。
  • eval() 通常比其他替代方法更慢,因为它必须调用 JS 解释器,而许多其他结构则可被现代 JS 引擎进行优化。此外,现代JavaScript解释器将javascript转换为机器代码。 这意味着任何变量命名的概念都会被删除。 因此,任意一个eval的使用都会强制浏览器进行冗长的变量名称查找,以确定变量在机器代码中的位置并设置其值。

Function 是替代 eval 的一个好的方法。

Function
代码语言:javascript
复制
new Function ([arg1[, arg2[, ...argN]],] functionBody)

每个 JavaScript 函数实际上都是一个 Function 对象。运行 (function(){}).constructor === Function // true 便可以得到这个结论。

eval 不同的是,Function 创建的函数只能在全局作用域中运行。

代码语言:javascript
复制
function test() {
  let x = 2, y = 4;
  console.log(new Function('return x + y')());  // 直接调用,使用全局作用域,throws ReferenceError
}
Nodejs 动态执行脚本

通过 node 的核心模块 vm 来实现。vm可以使用v8的Virtual Machine contexts动态地编译和执行代码,而代码的执行上下文是与当前进程隔离的,但是这里的隔离并不是绝对的安全,不完全等同浏览器的沙箱环境。

  1. vm.runInContext(code, contextifiedObject[, options]) 在指定的 contextifiedObject 的上下文里执行它并返回其结果。 被执行的代码无法获取本地作用域。 contextifiedObject 必须是事先被 vm.createContext() 方法上下文隔离化过的对象。 const vm = require('vm') const contextObject = { a: 1 } vm.createContext(contextObject) const result = vm.runInContext('a += 1; b = 3', contextObject) console.log(result) // 3 { a: 2, b: 3 }
  2. vm.runInNewContext(code[, contextObject[, options]]) 给指定的 contextObject(若为 undefined,则会新建一个contextObject)提供一个隔离的上下文, 再在此上下文中执行编译的 code,最后返回结果。 运行中的代码无法获取本地作用域。 const vm = require('vm') const result = vm.runInNewContext('a += 1; b = 3', {a: 1}) console.log(result) // 3 { a: 2, b: 3 }
  3. vm.runInThisContext(code[, options]) 在当前的 global 对象的上下文中编译并执行 code,最后返回结果。 运行中的代码无法获取本地作用域,但可以获取当前的 global 对象。 global.a = 1 const result = vm.runInThisContext('a += 1') console.log(result) vm.runInThisContext() 更像是间接的执行 eval(), 就像 (0,eval)('code')
  4. eval() Nodejs 中同样可以使用 eval 函数,但性能和安全性有差异。请查看 https://odino.org/eval-no-more-understanding-vm-vm2-nodejs/
  5. vm2 Node.js 的高级 vm/sandbox,https://github.com/patriksimek/vm2
上下文隔离化

所有用 Node.js 所运行的 JavaScript 代码都是在一个“上下文”的作用域中被执行的。在 V8 中,一个上下文是一个执行环境,它允许分离的,无关的 JavaScript 应用在一个 V8 的单例中被运行。 必须明确地指定用于运行所有 JavaScript 代码的上下文。

代码语言:javascript
复制
vm.createContext([contextObject[, options]])

contextObject参数(如果 contextObjectundefined,则为新创建的对象)在内部与 V8 上下文的新实例相关联。 该 V8 上下文提供了使用 vm 模块的方法运行的 code 以及可在其中运行的隔离的全局环境。

使用场景

动态执行字符串代码。vue ssr 中是通过 runInNewContext 实现的( Vue SSR 指南)。

参考地址

  • https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/eval
  • http://nodejs.cn/api/vm.html
  • https://ssr.vuejs.org/zh/api/#runinnewcontext
  • https://odino.org/eval-no-more-understanding-vm-vm2-nodejs/
  • https://github.com/patriksimek/vm2
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-04-12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 浏览器中动态执行脚本
    • eval()
      • Function
        • Nodejs 动态执行脚本
          • 上下文隔离化
          • 使用场景
          • 参考地址
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档