ScriptEngine JavaScript Doesn't Support Includes?

5.1k Views Asked by At

I've got some code like the following.

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

But that throws the following error

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)

I can do indexOf(1) instead and that seems to work, but is there a reason why I don't have access to includes with this parser?

2

There are 2 best solutions below

2
On BEST ANSWER

String.prototype.includes is specified in ECMAScript 2015 ( ECMA-262 6th Edition). Nashorn engine implements ECMA-262 Edition 5.1). See also 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 has a polyfill for String.prototype.includes. I checked that the polyfill works with Nashorn engine.

0
On

Nashorn does not support includes() because it implemented an earlier version of the JavaScript spec before includes() was added. You can polyfill (aka shim) support for includes() into Nashorn using the reference on Mozilla's website.

The original question was for doing includes() on a JavaScript array. A previous answer was for JavaScript string, so not correct. Here is a snippet of JUnit test code that shows how to use polyfills for both arrays and strings.

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