「小技巧」如何在JS中运行一串字符串

334 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情

最近做的需求牵扯到通过内部配置文件来在不发包的程度下做到最灵活修改,众所周知拉下来的配置文件一般都是js或者json,所以整理了下载js中运行字符串代码的5个方法,希望对大家有所帮助。

以下提到的所有方法,务必注意注入攻击 即使是node沙箱,也并不是一个完全安全的模块。

浏览器/Node通用

eval

  • 可以访问&修改局部变量
  • 可以访问全局变量 - console可以打出来
let x = 10;

function main() {
  let x = 20;
  eval('x = 30; console.log("console执行了")')
  console.log(x)
}

main();
console.log(x);

// console执行了
// 30
// 10

new Function

  • new Function只会被声明在全局中,访问不了闭包变量
  • 只能访问&修改位于最上层的变量,也因此,在浏览器和node中,new Function的表现不一致
  • 可以访问全局变量 - console可以打出来
// 浏览器环境
let x = 10;

function createFunction1() {
  let x = 20;
  const innerFunction = function() {
    let x = 0
    const res = new Function("x = 30; console.log('运行了newFunction')");
    res()
    console.log(x) // 0
  }
  innerFunction()
  console.log(x); // 20
}

createFunction1();
console.log(x); // 30
// node环境
let x = 10;

function createFunction1() {
  let x = 20;
  const innerFunction = function() {
    let x = 0
    const res = new Function("x = 30; console.log('运行了newFunction')");
    res()
    console.log(x) // 0
  }
  innerFunction()
  console.log(x); // 20
}

createFunction1();
console.log(x); // 10 - new Function拿到的并不是这一层的,而是更加顶层的

blob

  • new Function 一样,无法访问&修改闭包变量,只能访问&修改最上层变量
let x = 10
function test() {
    let x = 20
    const blobText = "console.log(x); x = 30; console.log('运行了blobText'); console.log(x)"
    const blob = new Blob([blobText], {type: 'application/javascript'})
    const blobUrl = URL.createObjectURL(blob)
    const scriptEl = document.createElement('script')
    scriptEl.src = blobUrl
    document.body.append(scriptEl)

    scriptEl.onload = () => {
      console.log('onload', x)
    }
}
test()

// 10
// 运行了blobText
// 30
// onload 20

Node

以上提到的两种方式都可以随意操纵本地变量,如果想要一个相对纯净的上下文,vm虚拟机了解一下~nodejs.cn/api/vm.html

runInContext/runInThisContext/runInNewContext

  • 要使用这三者,都需要先通过 new vm.script('一串代码字符串')将字符串转成Script对象
  • 放在一起看三者的区别:
    • runInContext:
      • 需要一个使用 vm.createContext({})创建的上下文
      • 无法获取到全局对象
    • runInNewContext:
      • 上下文对象可以为空
      • 无法获取到全局对象
    • runInThisContext:
      • 上下文对象为 当前global
      • 可以访问&修改全局变量

/**
 * runInContext
 **/
const vm = require("vm");
let a = 1;
const scriptContext = { a };
const script = new vm.Script("a=2; console.log(a);");
vm.createContext(scriptContext);
script.runInContext(scriptContext); // console不会打出来
console.log(scriptContext.a); // 2
console.log(a); // 1
/**
 * runInThisContext 运行代码无权访问局部作用域,但可以访问当前 global 对象。
 **/
const vm = require("vm");
let a = 1;
global.b = 1;
const script = new vm.Script("a=2; b = 2; console.log(a);");
script.runInThisContext(); // console会打出来, 2
console.log(global.b); // 1
console.log(a); // 1
/**
 * runInNewContext
 **/
const vm = require("vm");
let a = 1;
const scriptContext = { a };
const script = new vm.Script("a=2; console.log(a);");
script.runInNewContext(); // 上下文环境可以为空,但console也不会打出来
console.log(scriptContext.a); // 1
console.log(a); // 1