【问题标题】:Create helper function to run a function in an isolated scope创建辅助函数以在隔离范围内运行函数
【发布时间】:2018-06-12 18:55:11
【问题描述】:

此代码有效:

  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();
 }));

其中 isolated 是一个辅助函数,类似于:

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();
 }));

【问题讨论】:

  • 我很惊讶没有人问你为什么要这样做。这有什么用?
  • 为什么不在stringify操作后使用正则表达式剪掉函数的内部?
  • @patrick 用于测试。独立的函数意味着你有一个干净的范围
  • 我知道什么是独立函数,但你试图显式地做 v8 已经隐式做的事情似乎很奇怪。
  • @AlexanderMills 在编写函数时未引用外部范围内的任何变量,v8 将自动将其标记为隔离并应用相关优化。您可以通过在 Chrome 上的函数中设置断点来确认这一点。如果您这样做并查看断点处的可用范围,您将无法从函数范围内访问外部范围中的变量值,因为 v8 在 JIT 编译期间将该函数标记为隔离。

标签: javascript node.js


【解决方案1】:

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

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 是唯一难以实现且不完整的部分。这是必要的,因为作为with 语句的范围提供的Proxy 不会拦截对this 关键字的访问,因此还必须显式包装上下文以拦截和抛出@987654331 的任何间接使用@ 在一个孤立的箭头函数内。

【讨论】:

  • 很酷,我可能会偷那个..pop 到 NPM 模块中并编写一些测试,如果你不介意,我会将它作为我的依赖项
  • @AlexanderMills 这是很多免费的工作。如果您不介意,我宁愿您只使用 sn-p 并将其归功于该答案的内联评论。
  • 确定没问题,我觉得你已经完成了大部分工作
  • 如果您可以在新答案的开头添加更多信息,那将是很好的 - 我没有在高级别关注它是如何工作的
  • 您仍然需要Function,原因与您在旧答案中需要它的原因相同。只是,您可以方便地将其包装在一个额外的作用域中,该作用域专用于使用 with (new Proxy(...)) 语句拦截作用域变量引用,并使用专用于抛出任何类型的使用的上下文调用函数构造函数。
【解决方案2】:

我会简单地使用indexOf('{')lastIndexOf('}')

const yourFunction = h => {
    console.log(h);
    h.ctn();
};

const fnText = yourFunction.toString();
const body = fnText.substring(fnText.indexOf('{') + 1, fnText.lastIndexOf('}'));

console.log(body);

知道这不会涵盖没有主体的箭头函数:

const fn = k => k + 1

【讨论】:

  • yeah..good call wrt to the arrow functions with {}...我想不出解析它们的好方法,你能吗?问题是如果省略了周围的字符,第一个“{”字符可能会出现在错误的位置。我认为正则表达式可以做到这一点。基本上检查 => 之后的第一个字符是否是 { 或其他内容。
【解决方案3】:

好吧,这行得通,这并不难。 我们只是假设第一个和最后一个括号是函数体的轮廓。

const isolated = function(fn){
  const str = fn.toString();
  const first = str.indexOf('{') + 1;
  const last = str.lastIndexOf('}');
  const body = str.substr(first, last-first);
  const paramNames = ['h'];
  return new Function(...paramNames.concat(body));
};

上面我们假设唯一的参数叫做“h”,但是你需要找到函数参数解析器。我以前用过require('function-arguments')

【讨论】:

  • 你也可以使用const body = str.substring(first, last);
  • ahh substr 与 substring 不同吗?
  • substr(indexStart, length)substring(indexStart, indexEnd)
  • 很好,我不知道有什么区别,gtk
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-08
  • 2011-11-12
  • 2021-11-26
  • 1970-01-01
  • 1970-01-01
  • 2020-10-21
相关资源
最近更新 更多