一尘不染

创建帮助函数以在隔离的范围内运行函数

javascript

此代码有效:

  it.cb(h => {
    console.log(h);
    h.ctn();
  });

  it.cb(new Function(
    'h', [
      'console.log(h)',
      'h.ctn()'
    ]
    .join(';')
  ));

这两个测试用例基本相同。但是用这样的数组构造一个字符串很麻烦,而且您无法获得静态分析。所以我当时想做的是这样的:

 it.cb(isolated(h => {
    console.log(h);
    h.ctn();
 }));

其中孤立的是一个类似于以下内容的辅助函数:

const isolated = function(fn){
   const str = fn.toString();
   const paramNames = getParamNames(str);
   return new Function(...paramNames.concat(str));
};

最大的问题是Function.prototype.toString()给您整个功能。有谁知道从函数的字符串表示形式获取函数体的好方法?

更新:PRoberts在问这是什么目的,目的很简单:

 const foo = 3;

 it.cb(isolated(h => {
    console.log(foo);  // this will throw "ReferenceError: foo is not defined"
    h.ctn();
 }));

阅读 531

收藏
2020-07-09

共1个答案

一尘不染

我写了一个版本,isolated()可以处理任何非bind用户定义的函数表达式,并为范围访问抛出自定义错误:

function isolated (fn) {
  return new Function(`
    with (new Proxy({}, {
      has () { return true; },
      get (target, property) {
        if (typeof property !== 'string') return target[property];
        throw new ReferenceError(property + ' accessed from isolated scope');
      },
      set (target, property) {
        throw new ReferenceError(property + ' accessed from isolated scope');
      }
    })) return ${Function.prototype.toString.call(fn)}
  `).call(new Proxy(function () {}, new Proxy({}, {
    get() { throw new ReferenceError('this accessed from isolated scope'); }
  })));
}

// test functions
[
  () => arguments, // fail
  () => this, // pass, no way to intercept this
  () => this.foo, // fail
  () => this.foo = 'bar', // fail
  () => this(), // fail
  () => new this, // fail
  h => h, // pass
  h => i, // fail
  (a, b) => b > a ? b : a, // pass
].forEach(fn => {
  const isolate = isolated(fn);
  console.log(isolate.toString());

  try {
    isolate();
    console.log('passed');
  } catch (error) {
    console.log(`${error.name}: ${error.message}`);
  }
})

与尝试解析用户定义函数的参数和主体相比,此实现稍微简单一些,因此不容易出错。

该with语句是一种相对简单的方法,用于在强制隔离的函数中捕获所有作用域的引用并抛出ReferenceError。它通过Proxy使用一个get陷阱将一个中间变量插入到作用域中来实现,该陷阱会拦截所访问的作用域变量名。

Proxy作为函数的背景是,这是一个有点棘手来实现,也残缺的唯一部分传递。这是必要的,因为Proxy提供给with语句的作用域不会拦截对this关键字的访问,因此还必须显式包装上下文,以拦截并间接使用this隔离的箭头函数内部的任何间接用法。

2020-07-09