ScriptEngine JavaScript 不支持包含?

ScriptEngine JavaScript Doesn't Support Includes?

我有一些代码如下。

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
engine.eval("[1, 2, 3].includes(1)");

但是会抛出以下错误

javax.script.ScriptException: TypeError: [1, 2, 3].includes is not a function in <eval> at line number 1
    at jdk.nashorn.api.scripting.NashornScriptEngine.throwAsScriptException(NashornScriptEngine.java:470)
    at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:454)
    at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:406)
    at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:402)
    at jdk.nashorn.api.scripting.NashornScriptEngine.eval(NashornScriptEngine.java:155)
    at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264)

我可以改用 indexOf(1),这似乎可行,但我无法访问此解析器的 includes 是不是有什么原因?

String.prototype.includes 在 ECMAScript 2015(ECMA-262 第 6 版)中指定。 Nashorn 引擎实现 ECMA-262 版本 5.1)。另见 http://www.ecma-international.org/ecma-262/6.0/#sec-string.prototype.includes

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes 有一个 polyfill String.prototype.includes。我检查了 polyfill 是否适用于 Nashorn 引擎。

Nashorn 不支持 includes(),因为它在添加 includes() 之前实现了 JavaScript 规范的早期版本。您可以使用 Mozilla 网站上的参考资料将对 includes() 的 polyfill(aka shim)支持添加到 Nashorn 中。

最初的问题是在 JavaScript 数组上执行 includes()。之前的答案是针对 JavaScript 字符串的,因此不正确。下面是一段 JUnit 测试代码,展示了如何对数组和字符串使用 polyfill。

    // Copied from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes#Polyfill
    public static final String NASHORN_POLYFILL_STRING_PROTOTYPE_INCLUDES = "if (!String.prototype.includes) { Object.defineProperty(String.prototype, 'includes', { value: function(search, start) { if (typeof start !== 'number') { start = 0 } if (start + search.length > this.length) { return false } else { return this.indexOf(search, start) !== -1 } } }) }";
    // Copied from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes#Polyfill
    public static final String NASHORN_POLYFILL_ARRAY_PROTOTYPE_INCLUDES  = "if (!Array.prototype.includes) { Object.defineProperty(Array.prototype, 'includes', { value: function(valueToFind, fromIndex) { if (this == null) { throw new TypeError('\"this\" is null or not defined'); } var o = Object(this); var len = o.length >>> 0; if (len === 0) { return false; } var n = fromIndex | 0; var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0); function sameValueZero(x, y) { return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y)); } while (k < len) { if (sameValueZero(o[k], valueToFind)) { return true; } k++; } return false; } }); }";

    @Test
    public void testStringIncludesWithPolyfill() throws Exception {
        runScript(NASHORN_POLYFILL_STRING_PROTOTYPE_INCLUDES, "'[1, 2, 3]'.includes(2)");
    }

    @Test(expected=javax.script.ScriptException.class)
    public void testStringIncludesWithoutPolyfill() throws Exception {
        runScript(null, "'[1, 2, 3]'.includes(2)");
    }

    @Test
    public void testArrayIncludesWithPolyfill() throws Exception {
        runScript(NASHORN_POLYFILL_ARRAY_PROTOTYPE_INCLUDES, "[1, 2, 3].includes(2)");
    }

    @Test(expected=javax.script.ScriptException.class)
    public void testArrayIncludesWithoutPolyfill() throws Exception {
        runScript(null, "[1, 2, 3].includes(2)");
    }

    private void runScript(final String polyfill, final String booleanExpression) throws ScriptException {
        final ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
        final ScriptEngine        scriptEngine        = scriptEngineManager.getEngineByName("nashorn");
        Assert.assertNotNull(scriptEngine);
        if (null != polyfill) {
            scriptEngine.eval(polyfill);
        }
        final Object booleanExpressionResult = scriptEngine.eval(booleanExpression);    // returns Boolean object
        Assert.assertNotNull(booleanExpressionResult);
        Assert.assertEquals(booleanExpressionResult.getClass(), Boolean.class);
        System.out.println(booleanExpression + " = " + booleanExpressionResult.toString());
    }