如何在严格模式下替换“with”语句

How to replace `with` statement in strict mode

提问人:kungfooman 提问时间:8/28/2023 更新时间:8/28/2023 访问量:89

问:

此代码以最佳方式工作且易于理解:

function evalInScope(js, contextAsScope) {
    //# Return the results of the in-line anonymous function we .call with the passed context
    return function() {
        with(this) {
            return eval(js);
        };
    }.call(contextAsScope);
}
evalInScope("a + b", {a: 1, b: 2}); // 3 obviously, but fails in strict mode!

然而,“聪明”的大脑决定在没有适当替换的情况下删除该声明。with

问:如何让它在自动处于严格模式的 ES6 中再次工作?

JavaScript ecmascript-6 with-statement 严格模式

评论

1赞 CBroe 8/28/2023
stackoverflow.com/q/9781285/1427878stackoverflow.com/q/543533/1427878 有一些方法,也许其中一种方法适合您的情况。
5赞 FZs 8/28/2023
首先,ES6 不会自动处于严格模式,仅在 ESM 模块和类主体中。其次,从严格模式中删除是有充分理由的。你的例子对我来说似乎是一个 XY 问题。您想通过该功能实现什么?你在评估什么?(如果它是硬编码的,请不要使用;如果是用户输入,那么它是危险的,你应该解析和解释它)withevaleval
2赞 FZs 8/28/2023
@kungfooman好吧。有几种方法可以绕过它。1. 您可以在 CommonJS 模块中实现此功能(然后您也可以从 ESM 中使用该模块),其中存在非严格环境。2. Node vm 模块中有一些有趣的 API,它们可能允许你做这样的事情。3. 您可以使用构造函数代替 ,这允许您传入动态命名的参数。Functioneval
1赞 kungfooman 8/28/2023
@FZs 非常感谢,我只是接受了@Bergi的回答。模块 API 看起来非常有趣,我也可能会尝试一下以了解更多信息。这种方法似乎是最简单且在大多数环境中开箱即用的,竖起大拇指vmFunction
1赞 FZs 8/28/2023
@kungfooman哇,Bergi 的构造函数方法与我的想法不同。Function

答:

0赞 Cerbrus 8/28/2023 #1

问题是你正在尝试做一些严格模式特别阻止的事情。

所以,基本上,你想做的事情是不可能的。

你要么不得不选择不使用使用严格模式,要么你将不得不解决使用(我个人建议永远不要使用......evaleval

5赞 Bergi 8/28/2023 #2

不要使用 ,而是创建一个新的函数。它不会继承词法严格模式 - 更好的是,它不会继承所有函数范围和模块范围的变量:eval

"use strict";

function evalInScope(js, contextAsScope) {
  return new Function(`with (this) { return (${js}); }`).call(contextAsScope);
}

console.log(evalInScope("a + b", { a: 1, b: 2 })); // 3

此外,您不会获得奇怪的“(最后一个)语句结果”返回值,但可以将代码限制为表达式,也可以在代码本身中包含语句。evaljsreturn


或者,如果您实际上不需要使用具有所有复杂性的语句,而只是想使一组动态的常量变量可供 ed 代码使用,则只需动态地为这些常量生成代码即可。这允许代码在严格模式下甚至:withevaleval

"use strict";

function evalInScope(js, contextAsScope) {
  return new Function(
    `"use strict";
    const {${Object.keys(contextAsScope).join(', ')}} = this;
    return (${js});`
  ).call(contextAsScope);
}

console.log(evalInScope("a + b", { a: 1, b: 2 })); // 3

或者,如果代码不使用关键字本身,也许也this

"use strict";

function evalInScope(js, contextAsScope) {
  return new Function(
    '{' + Object.keys(contextAsScope).join(', ') + '}',
    `return (${js});`
  )(contextAsScope);
}

console.log(evalInScope("a + b", { a: 1, b: 2 })); // 3

评论

0赞 kungfooman 8/28/2023
非常感谢,您的答案始终是有根据的!刚刚测试过,它运行良好。为了更短,是否可以删除第一个参数(这是和之前的参数),或者您对此有不好的体验?就像也不需要''newFunctionErrornew Error
1赞 Bergi 8/28/2023
这不是必需的。我没有费心去阅读我链接的 mdn 页面:-)我记得这样的事情,但我总是使用双参数版本,并为参数声明传递一个逗号分隔的字符串。
0赞 Cerbrus 8/28/2023
所以,答案是基本上打破严格模式,用任何方式......我知道这是如何工作的,但那是......时髦。with
0赞 Bergi 8/28/2023
@Cerbrus 是的,如果你真的需要使用(例如,使用代理来拦截所有非局部变量访问),那么你就无法绕过它。但实际上有更好的方法可以在严格模式下工作,请参阅更新with
0赞 Cerbrus 8/28/2023
这些确实好多了!