C = class extends Array { } N = class { } N[Symbol.species] = function() { throw "this should never be called"; } function id(x) { return x; } testFunctions = [ [Array.prototype.concat, []], [Array.prototype.slice, [1,2]], [Array.prototype.splice, []], [Array.prototype.splice, [0,1]], [Array.prototype.map, [id]], [Array.prototype.filter, [id]] ]; objProp = Object.defineProperty; function funcThrows(func, args) { try { func.call(...args) return false; } catch (e) { return true; } } function test(testData) { "use strict"; let [protoFunction, args] = testData; let foo = new C(10); let n = new N(); // Test non-array ignores constructor. objProp(n, "constructor", { value: C }); let bar = protoFunction.call(n, ...args); if (!(bar instanceof Array) || bar instanceof N || bar instanceof C) throw Error(); objProp(foo, "constructor", { value: null }); if (!funcThrows(protoFunction, [foo, ...args])) throw "didn't throw"; // Test array defaults cases. foo = new C(10); objProp(C, Symbol.species, { value: undefined, writable: true}); bar = protoFunction.call(foo, ...args); if (!(bar instanceof Array) || bar instanceof C) throw Error(); C[Symbol.species] = null; bar = protoFunction.call(foo, ...args); if (!(bar instanceof Array) || bar instanceof C) throw Error(); // Test species is custom constructor. let called = false; function species(...args) { called = true; return new C(...args); } C[Symbol.species] = species; bar = protoFunction.call(foo, ...args); if (!(bar instanceof Array) || !(bar instanceof C) || !called) throw Error("failed on " + protoFunction); function speciesThrows() { throw Error(); } C[Symbol.species] = speciesThrows; if (!funcThrows(protoFunction, [foo, ...args])) throw "didn't throw"; C[Symbol.species] = true; if (!funcThrows(protoFunction, [foo, ...args])) throw "didn't throw"; } testFunctions.forEach(test);