Custom behavior

This section summarizes Duktape behavior which deviates from the E5.1 or other relevant specifications.

Duktape built-in and custom types

The Duktape built-in is (of course) non-standard and provides access to Duktape specific features. Also the buffer, pointer, and lightfunc types are custom.

Internal properties

Objects may have internal properties which are essentially hidden from normal code: they won't be enumerated or returned even by e.g. Object.getOwnPropertyNames(). Ordinary Ecmascript code cannot refer to such properties because the property keys intentionally use invalid UTF-8 (0xFF prefix byte).

"use duk notail" directive

The "use duk notail" directive is non-standard. It prevents a function from being tail called.

"const" treated mostly like "var"

The const keyword is supported with minimal non-standard semantics (officially defined in Ecmascript 6). See Const variables for more detail.

The global require() function for module loading

The require() built-in is non-standard, and provided for CommonJS-based module loading, see Modules.

Additional Error and Function object properties

See Error objects and Function objects.

Non-strict function instances don't have a caller property in the E5/E5.1 specification. Some real world code expects to have this property, so it can be enabled with the feature option DUK_OPT_NONSTD_FUNC_CALLER_PROPERTY.

Function statements

E5.1 does not allow a function declaration to appear outside program or function top level:

function test() {
    // point A
    try {
        throw new Error('test');
    } catch (e) {
        // This is a SyntaxError in E5.1
        function func() {
            print(typeof e);
        }
        // point B
    }
    // point C
}

These declarations are also referred to as "function statements", and appear quite often in real world code (including the test262 test suite), so they are allowed by Duktape. Unfortunately there are several semantics used by different Javascript engines. Duktape follows the V8 behavior for function statements:

As an illustration, the above example would behave as the following:

function test() {
    function func() {
        print(typeof e);
    }
 
    try {
        throw new Error('test');
    } catch (e) {
    }
}

func() in the above example would already be declared and callable in point A, and would not have access to the e binding in any of the points A, B, or C.

RegExp leniency

Although not allowed by E5.1, the following escape is allowed in RegExp syntax:

  /\$/       /* matches dollar literally, non-standard */
  /\u0024/   /* same, standard */

This escape occurs in real world code so it is allowed. (More leniency will be added in future versions to deal with real world RegExps; dollar escapes are not the only issue.)

Array.prototype.splice() when deleteCount not given

When deleteCount (the 2nd argument) is not given to Array.prototype.splice(), the standard behavior is to work as if the 2nd argument was undefined (or 0, which has the same behavior after coercions). A more real world compatible behavior is to treat the missing argument like positive infinity, i.e. to extend the splice operation to the end of the array.

Because the non-standard real world behavior is expected by much existing code, Duktape uses this behavior by default. The strict standards compliant behavior can be enabled with the feature option DUK_OPT_NO_NONSTD_ARRAY_SPLICE_DELCOUNT.

Array.prototype.concat() trailing non-existent elements

When the result of an array concat() would have trailing non-existent elements, the standard behavior is to ignore them so that they are not reflected in the result length. Real world behavior is to include them in the result value length. See test-bi-array-proto-concat-nonstd-trailing.js.

The real world behavior seems consistent in other engines (V8, Rhino, Spidermonkey at least), so Duktape uses the real world behavior by default. The strict standards compliant behavior can be enabled with the feature option DUK_OPT_NO_NONSTD_ARRAY_CONCAT_TRAILER.

Array.prototype.map() trailing non-existent elements

Similar issue as with Array.prototype.concat(), see test-bi-array-proto-map-nonstd-trailing.js. The strict standards compliant behavior can be enabled with the feature option DUK_OPT_NO_NONSTD_ARRAY_MAP_TRAILER.

Setter/getter key argument

Ecmascript standard behavior is that setters and getters are not given the name of the property being accessed. This prevents reusing a single setter or a getter for multiple properties; separate functions are needed for each property which is sometimes inconvenient and wastes memory.

Duktape provides the property key name as a non-standard additional argument to setter and getter functions. See test-dev-nonstd-setget-key-argument.js and Property virtualization for more discussion. The strict standards compliant behavior can be enabled with the feature option DUK_OPT_NO_NONSTD_ACCESSOR_KEY_ARGUMENT.

Object.setPrototypeOf and Object.prototype.__proto__ (ES6)

See Object.setPrototypeOf and Object.prototype.__proto__.

Proxy object (ES6)

See Proxy object (subset).

JSON.stringify() escapes U+2028 and U+2029

JSON.stringify() standard behavior is to output U+2028 and U+2029 without escaping. This leads to counterintuitive behavior when the output is used in a web page or parsed with eval(): the U+2028 and U+2029 characters are considered line terminators which leads to a syntax error (unterminated string). Duktape escapes U+2028 and U+2029 by default to avoid this issue; you can turn on the compliant behavior with the feature option DUK_OPT_NO_NONSTD_JSON_ESC_U2028_U2029.

String.fromCharCode() accepts 32-bit codepoints

String.fromCharCode() standard behavior is to use ToUInt16() coercion for codepoint values. Duktape uses ToUint32() by default to better support non-BMP strings. You can force the compliant behavior with the feature option DUK_OPT_NO_NONSTD_STRING_FROMCHARCODE_32BIT.

Array instance numeric index writes

By default Duktape provides a fast path for writing to Array instances. The fast path is active when numeric indices are used (e.g. arr[7] = 'foo') and a few internal conditions are met. When the fast path is taken, Duktape doesn't check Array.prototype for conflicting properties (these are very rare in practical code), which makes common array writes faster. The behavior is non-compliant, but there's no outward difference unless Array.prototype has properties with numeric keys. You can turn on the compliant behavior with the feature option DUK_OPT_NO_NONSTD_ARRAY_WRITE. See the following for more details on the fast path behavior: test-misc-array-fast-write.js.

TypedArray binding

Duktape provides a Khronos/ES6 TypedArray binding. The scope of the binding is the Khronos specification, not the full ES6 specification. There are some differences to Khronos/ES6, including:

Node.js Buffer binding

Duktape provides a Node.js-like Buffer binding. There are some differences between the Node.js behavior and Duktape behavior. These differences include: