Array Methods
Array prototype methods and statics in SSJS — which work natively, which are partial, and which are missing, with safe ES3/ES5 alternatives and polyfill links.
Each member below is tagged with the ECMAScript edition that standardized it: (ES3), (ES5), or (ES6). Methods that need a polyfill link to Polyfills.
Status legend
| Icon | Meaning |
|---|---|
| ✅ Works | Available and behaves as expected |
| ⚠️ Partial | Available but with a documented caveat or bug |
| ❌ Missing | Not available — use the workaround / polyfill |
Members
| Member | ES | Status | Notes |
|---|---|---|---|
push(...items) |
ES3 | ✅ Works | |
pop() |
ES3 | ✅ Works | |
shift() |
ES3 | ✅ Works | |
unshift(...items) |
ES3 | ✅ Works | |
concat(...arrays) |
ES3 | ✅ Works | |
join(separator) |
ES3 | ✅ Works | |
reverse() |
ES3 | ✅ Works | |
length |
ES3 | ✅ Works | |
toLocaleString() |
ES3 | ✅ Works | |
slice(start, end) |
ES3 | ⚠️ Partial | Positive/negative indices work; the no-arg slice() throws — see Polyfills |
sort(compareFn) |
ES3 | ⚠️ Partial | Works with a compare function; the no-arg sort() throws — see Polyfills |
splice(start, deleteCount, ...items) |
ES3 | ⚠️ Partial | Only splice(start, deleteCount) works; splice(start) throws and the insert form is broken — see Polyfills |
indexOf(searchValue, fromIndex) |
ES5 | ❌ Missing | See Polyfills |
lastIndexOf(searchValue, fromIndex) |
ES5 | ⚠️ Partial | Broken — always returns -1; see Polyfills |
forEach(fn) |
ES5 | ❌ Missing | Use a for loop or the polyfill |
map(fn) |
ES5 | ❌ Missing | Use a for loop or the polyfill |
filter(fn) |
ES5 | ❌ Missing | Use a for loop or the polyfill |
reduce(fn, initial) |
ES5 | ❌ Missing | Use a for loop or the polyfill |
reduceRight(fn, initial) |
ES5 | ❌ Missing | Use a for loop or the polyfill |
some(fn) |
ES5 | ❌ Missing | Use a for loop or the polyfill |
every(fn) |
ES5 | ❌ Missing | Use a for loop or the polyfill |
find(fn) |
ES6 | ❌ Missing | Use a for loop or the polyfill |
findIndex(fn) |
ES6 | ❌ Missing | Use a for loop or the polyfill |
includes(searchValue) |
ES6 | ❌ Missing | Use indexOf(x) !== -1 or the polyfill |
fill(value, start, end) |
ES6 | ❌ Missing | See Polyfills |
copyWithin(target, start, count) |
ES6 | ❌ Missing | See Polyfills |
entries() |
ES6 | ❌ Missing | See Polyfills |
keys() |
ES6 | ❌ Missing | Use a standard index for loop |
values() |
ES6 | ❌ Missing | Use a standard index for loop |
at(index) |
ES6 | ❌ Missing | Use arr[i] (and arr[arr.length + i] for negative i) |
flat(depth) |
ES6 | ❌ Missing | Concatenate nested arrays manually in a loop |
flatMap(fn) |
ES6 | ❌ Missing | Build the result with a for loop and push |
findLast(fn) |
ES6 | ❌ Missing | Iterate from the end with a for loop |
Array.isArray(value) |
ES5 | ❌ Missing | See Polyfills |
Array.of(...items) |
ES6 | ❌ Missing | See Polyfills |
Array.from(source) |
ES6 | ❌ Missing | Build the array with a for loop over the source |
push
(ES3) — ✅ Works. Appends one or more items to the end of the array and returns the new length.
var arr = [1, 2, 3];
arr.push(4); // [1, 2, 3, 4]
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: push(...items)
*
* Proves:
* 1. push is available (typeof "function").
* 2. Appending a single item mutates the array in place: [1,2,3] -> [1,2,3,4].
* 3. push returns the NEW length of the array.
* 4. Multiple items can be appended in one call.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
var pushArr = [1, 2, 3];
assert("typeof [].push is function", typeof pushArr.push, "function");
var pushRet = pushArr.push(4);
assert("[1,2,3].push(4) makes [1,2,3,4]", pushArr.join(","), "1,2,3,4");
assert("push returns the new length 4", pushRet, "4");
var pushArr2 = [1];
var pushRet2 = pushArr2.push(2, 3);
assert("push(2,3) appends both items", pushArr2.join(","), "1,2,3");
assert("push(2,3) returns the new length 3", pushRet2, "3");
</script>
pop
(ES3) — ✅ Works. Removes and returns the last item.
var arr = [1, 2, 3];
var last = arr.pop(); // 3, arr = [1, 2]
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: pop()
*
* Proves:
* 1. pop is available (typeof "function").
* 2. pop RETURNS the last item: [1,2,3].pop() === 3.
* 3. pop MUTATES the array in place, leaving [1,2].
* 4. pop on an empty array returns undefined.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
var popArr = [1, 2, 3];
assert("typeof [].pop is function", typeof popArr.pop, "function");
var popLast = popArr.pop();
assert("[1,2,3].pop() returns 3", popLast, "3");
assert("after pop the array is [1,2]", popArr.join(","), "1,2");
assert("after pop the length is 2", popArr.length, "2");
var popEmpty = [];
assert("[].pop() is undefined", typeof popEmpty.pop(), "undefined");
</script>
shift
(ES3) — ✅ Works. Removes and returns the first item.
var arr = [1, 2, 3];
var first = arr.shift(); // 1, arr = [2, 3]
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: shift()
*
* Proves:
* 1. shift is available (typeof "function").
* 2. shift RETURNS the first item: [1,2,3].shift() === 1.
* 3. shift MUTATES the array in place, leaving [2,3].
* 4. shift on an empty array returns undefined.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
var shiftArr = [1, 2, 3];
assert("typeof [].shift is function", typeof shiftArr.shift, "function");
var shiftFirst = shiftArr.shift();
assert("[1,2,3].shift() returns 1", shiftFirst, "1");
assert("after shift the array is [2,3]", shiftArr.join(","), "2,3");
assert("after shift the length is 2", shiftArr.length, "2");
var shiftEmpty = [];
assert("[].shift() is undefined", typeof shiftEmpty.shift(), "undefined");
</script>
unshift
(ES3) — ✅ Works. Prepends one or more items and returns the new length.
var arr = [1, 2, 3];
arr.unshift(0); // arr = [0, 1, 2, 3]
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: unshift(...items)
*
* Proves:
* 1. unshift is available (typeof "function").
* 2. Prepending mutates the array in place: [1,2,3] -> [0,1,2,3].
* 3. unshift returns the NEW length.
* 4. Several items can be prepended in one call, keeping their order.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
var unshiftArr = [1, 2, 3];
assert("typeof [].unshift is function", typeof unshiftArr.unshift, "function");
var unshiftRet = unshiftArr.unshift(0);
assert("[1,2,3].unshift(0) makes [0,1,2,3]", unshiftArr.join(","), "0,1,2,3");
assert("unshift returns the new length 4", unshiftRet, "4");
var unshiftArr2 = [3];
var unshiftRet2 = unshiftArr2.unshift(1, 2);
assert("unshift(1,2) prepends in order", unshiftArr2.join(","), "1,2,3");
assert("unshift(1,2) returns the new length 3", unshiftRet2, "3");
</script>
concat
(ES3) — ✅ Works. Returns a new array combining the array with the given arrays/values.
var combined = [1, 2].concat([3, 4]); // [1, 2, 3, 4]
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: concat(...arrays)
*
* Proves:
* 1. concat is available (typeof "function").
* 2. [1,2].concat([3,4]) returns a new [1,2,3,4].
* 3. concat does NOT mutate the receiver.
* 4. Plain values and several arrays can be concatenated in one call.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
var concatBase = [1, 2];
assert("typeof [].concat is function", typeof concatBase.concat, "function");
var concatOut = concatBase.concat([3, 4]);
assert("[1,2].concat([3,4]) is [1,2,3,4]", concatOut.join(","), "1,2,3,4");
assert("concat leaves the receiver unchanged", concatBase.join(","), "1,2");
assert("concat returns a new array (not the receiver)", concatOut === concatBase, "false");
assert("concat accepts plain values", [1].concat(2, 3).join(","), "1,2,3");
assert("concat accepts several arrays", [1].concat([2], [3, 4]).join(","), "1,2,3,4");
</script>
join
(ES3) — ✅ Works. Joins all elements into a string using the given separator.
["Hello", "World"].join(", "); // "Hello, World"
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: join(separator)
*
* Proves:
* 1. join is available (typeof "function").
* 2. ["Hello","World"].join(", ") returns "Hello, World".
* 3. join returns a string.
* 4. The no-argument form uses the default "," separator.
* 5. An empty array joins to the empty string.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
var joinArr = ["Hello", "World"];
assert("typeof [].join is function", typeof joinArr.join, "function");
assert("['Hello','World'].join(', ') is 'Hello, World'", joinArr.join(", "), "Hello, World");
assert("join returns a string", typeof joinArr.join(", "), "string");
assert("join() defaults to a comma separator", [1, 2, 3].join(), "1,2,3");
assert("join('') concatenates without a separator", [1, 2, 3].join(""), "123");
assert("[].join(',') is the empty string", [].join(","), "");
</script>
reverse
(ES3) — ✅ Works. Reverses the array in place.
var arr = [1, 2, 3];
arr.reverse(); // [3, 2, 1]
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: reverse()
*
* Proves:
* 1. reverse is available (typeof "function").
* 2. [1,2,3].reverse() reverses IN PLACE, giving [3,2,1].
* 3. reverse returns the same array object it mutated.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
var revArr = [1, 2, 3];
assert("typeof [].reverse is function", typeof revArr.reverse, "function");
var revRet = revArr.reverse();
assert("[1,2,3].reverse() gives [3,2,1]", revArr.join(","), "3,2,1");
assert("reverse mutates in place (same object returned)", revRet === revArr, "true");
assert("reverse of a 2-item array", ["a", "b"].reverse().join(","), "b,a");
</script>
length
(ES3) — ✅ Works. The number of elements in the array.
[1, 2, 3].length; // 3
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: length
*
* Proves:
* 1. [1,2,3].length is 3 and is a number.
* 2. An empty array has length 0.
* 3. length tracks mutations made through push and pop.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
var lenArr = [1, 2, 3];
assert("[1,2,3].length is 3", lenArr.length, "3");
assert("typeof length is number", typeof lenArr.length, "number");
assert("[].length is 0", [].length, "0");
lenArr.push(4);
assert("length is 4 after push", lenArr.length, "4");
lenArr.pop();
assert("length is 3 again after pop", lenArr.length, "3");
</script>
toLocaleString
(ES3) — ✅ Works. Returns a locale-specific string representation.
[1, 2, 3].toLocaleString(); // "1,2,3"
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: toLocaleString()
*
* Proves:
* 1. toLocaleString is available (typeof "function").
* 2. [1,2,3].toLocaleString() returns "1,2,3".
* 3. The result is a string.
* 4. An empty array yields the empty string.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
var tlsArr = [1, 2, 3];
assert("typeof [].toLocaleString is function", typeof tlsArr.toLocaleString, "function");
assert("[1,2,3].toLocaleString() is '1,2,3'", tlsArr.toLocaleString(), "1,2,3");
assert("toLocaleString returns a string", typeof tlsArr.toLocaleString(), "string");
assert("[].toLocaleString() is the empty string", [].toLocaleString(), "");
</script>
slice
(ES3) — ⚠️ Partial. Returns a shallow copy of a portion of the array. Positive and negative indices work correctly (slice(-2), slice(1, -1) return the expected ranges). The one bug is the no-argument form slice(), which throws Index was outside the bounds of the array. — always pass at least a start index (slice(0)) to copy the whole array, or apply the polyfill.
var arr = [0, 1, 2, 3, 4];
arr.slice(1, 3); // [1, 2]
arr.slice(-2); // [3, 4] — negative indices work
arr.slice(1, -1); // [1, 2, 3]
arr.slice(0); // copy of arr — use this instead of arr.slice()
// arr.slice() — no-arg form THROWS; use arr.slice(0) or the polyfill
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: slice(start, end) - PARTIAL
*
* Proves:
* 1. slice is available (typeof "function").
* 2. slice(1, 3) returns [1, 2] from [0,1,2,3,4].
* 3. NEGATIVE indices work correctly: slice(-2) -> [3,4], slice(1,-1) -> [1,2,3].
* 4. slice(0) copies the whole array and does not mutate the receiver.
* 5. DEVIATION: the no-argument form slice() THROWS
* "Index was outside the bounds of the array."
* (spec: slice() returns a shallow copy of the whole array).
* 6. The documented workaround slice(0) produces exactly that copy.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var sliceArr = [0, 1, 2, 3, 4];
assert("typeof [].slice is function", typeof sliceArr.slice, "function");
assert("slice(1,3) is [1,2]", sliceArr.slice(1, 3).join(","), "1,2");
assert("slice(-2) is [3,4] - negative start works", sliceArr.slice(-2).join(","), "3,4");
assert("slice(1,-1) is [1,2,3] - negative end works", sliceArr.slice(1, -1).join(","), "1,2,3");
assert("slice(0) copies the whole array", sliceArr.slice(0).join(","), "0,1,2,3,4");
assert("slice(0) returns a NEW array", sliceArr.slice(0) === sliceArr, "false");
assert("slice does not mutate the receiver", sliceArr.join(","), "0,1,2,3,4");
assertThrows("DEV slice() no-arg form throws (spec: full copy)", function () { var a = [0, 1, 2]; return a.slice(); });
assert("workaround slice(0) equals the original content", [7, 8, 9].slice(0).join(","), "7,8,9");
</script>
sort
(ES3) — ⚠️ Partial. Sorts in place. A supplied compare function works correctly (numeric and string comparators both sort as expected). The one bug is the no-argument form sort(), which throws Failed to compare two elements in the array. — always pass an explicit compare function, or apply the polyfill if you need the default lexicographic order.
var arr = [3, 1, 4, 1, 5];
arr.sort(function (a, b) { return a - b; }); // ascending — works
// arr.sort() — no-arg form THROWS; pass a compare function or use the polyfill
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: sort(compareFn) - PARTIAL
*
* Proves:
* 1. sort is available (typeof "function").
* 2. A numeric compare function sorts ascending, in place.
* 3. A string compare function sorts as expected.
* 4. sort returns the same array object it mutated.
* 5. DEVIATION: the no-argument form sort() THROWS
* "Failed to compare two elements in the array."
* (spec: sort() sorts lexicographically by default).
* 6. The documented workaround - always pass a compare function - works.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var sortArr = [3, 1, 4, 1, 5];
assert("typeof [].sort is function", typeof sortArr.sort, "function");
var sortRet = sortArr.sort(function (a, b) { return a - b; });
assert("numeric comparator sorts ascending", sortArr.join(","), "1,1,3,4,5");
assert("sort returns the same array object", sortRet === sortArr, "true");
var sortDesc = [3, 1, 4].sort(function (a, b) { return b - a; });
assert("numeric comparator sorts descending", sortDesc.join(","), "4,3,1");
var sortStr = ["pear", "apple", "fig"].sort(function (a, b) { if (a < b) { return -1; } if (a > b) { return 1; } return 0; });
assert("string comparator sorts alphabetically", sortStr.join(","), "apple,fig,pear");
assertThrows("DEV sort() no-arg form throws (spec: lexicographic sort)", function () { var a = [3, 1, 2]; return a.sort(); });
assert("workaround: always pass a comparator", [10, 9].sort(function (a, b) { return a - b; }).join(","), "9,10");
</script>
splice
(ES3) — ⚠️ Partial. Signature: splice(start[, deleteCount[, item1[, ...itemN]]]).
Only the two-argument delete form splice(start, deleteCount) works correctly (an over-large deleteCount is clamped to the remaining length). The one-argument form splice(start) throws Index was outside the bounds of the array. The insert form is also broken: as soon as a third argument is passed, the engine ignores start and deleteCount and overwrites from the left. Apply the polyfill from Polyfills for the one-argument delete form or any insert.
Show test script — proves splice(start) throws, the two-arg delete works with clamping, and the insert form overwrites from the left
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: splice(start, deleteCount, ...items) - PARTIAL
*
* Proves:
* 1. splice is available (typeof "function").
* 2. The two-argument DELETE form works: ["a","b","c","d"].splice(1,1)
* leaves ["a","c","d"].
* 3. An over-large deleteCount is clamped to the remaining length.
* 4. DEVIATION: the one-argument form splice(start) THROWS
* "Index was outside the bounds of the array."
* (spec: removes everything from start to the end).
* 5. DEVIATION: the INSERT form is broken - as soon as a third argument is
* passed the engine ignores start/deleteCount and overwrites from the left:
* ["a","b","c","d"].splice(1,1,"X") -> ["X","b","c","d"]
* (spec: ["a","X","c","d"])
* ["a","b","c","d"].splice(1,0,"B","C") -> ["B","C","c","d"]
* (spec: ["a","B","C","b","c","d"])
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var spArr = ["a", "b", "c", "d"];
assert("typeof [].splice is function", typeof spArr.splice, "function");
spArr.splice(1, 1);
assert("splice(1,1) deletes one item -> a,c,d", spArr.join(","), "a,c,d");
var spClamp = ["a", "b", "c", "d"];
spClamp.splice(1, 10);
assert("over-large deleteCount is clamped -> a", spClamp.join(","), "a");
assertThrows("DEV splice(start) one-arg form throws (spec: delete to end)", function () { var a = ["a", "b", "c", "d"]; return a.splice(2); });
var spIns = ["a", "b", "c", "d"];
spIns.splice(1, 1, "X");
assert("DEV splice(1,1,'X') overwrites from the left -> X,b,c,d (spec: a,X,c,d)", spIns.join(","), "X,b,c,d");
var spIns2 = ["a", "b", "c", "d"];
spIns2.splice(1, 0, "B", "C");
assert("DEV splice(1,0,'B','C') overwrites from the left -> B,C,c,d (spec: a,B,C,b,c,d)", spIns2.join(","), "B,C,c,d");
</script>
// Two-argument delete form works natively:
var arr = ["a", "b", "c", "d"];
arr.splice(1, 1); // ["a", "c", "d"]
arr.splice(1, 10); // deleteCount is clamped to the remaining length
// One-argument delete form REQUIRES the polyfill:
// arr.splice(2) — THROWS "Index was outside the bounds of the array."
// Insert form REQUIRES the polyfill (native engine overwrites from the left):
var arr2 = ["a", "b", "c", "d"];
arr2.splice(1, 1, "X"); // native gives ["X", "b", "c", "d"] (wrong)
arr2.splice(1, 0, "B", "C"); // native gives ["B", "C", "c", "d"] (wrong)
indexOf
(ES5) — ❌ Missing. Not available in SFMC. Apply the polyfill, or scan with a for loop.
function indexOf(arr, value) {
for (var i = 0; i < arr.length; i++) {
if (arr[i] === value) { return i; }
}
return -1;
}
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: indexOf(searchValue, fromIndex) - MISSING
*
* Proves:
* 1. Array.prototype.indexOf is NOT available in SFMC
* (typeof [].indexOf is "undefined"; spec (ES5): a function).
* 2. Calling it throws instead of returning an index.
* 3. The documented for-loop workaround returns the index of a match
* and -1 when the value is absent.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var ioArr = ["a", "b", "c"];
assert("DEV typeof [].indexOf is undefined (spec ES5: function)", typeof ioArr.indexOf, "undefined");
assertThrows("DEV [].indexOf('b') throws (spec: returns 1)", function () { var a = ["a", "b"]; return a.indexOf("b"); });
function indexOfPoly(arr, value) {
for (var i = 0; i < arr.length; i++) {
if (arr[i] === value) { return i; }
}
return -1;
}
assert("workaround indexOf finds 'b' at 1", indexOfPoly(ioArr, "b"), "1");
assert("workaround indexOf finds 'a' at 0", indexOfPoly(ioArr, "a"), "0");
assert("workaround indexOf returns -1 when absent", indexOfPoly(ioArr, "z"), "-1");
</script>
lastIndexOf
(ES5) — ⚠️ Partial. Present but broken — always returns -1. Apply the polyfill, or scan from the end with a for loop.
function lastIndexOf(arr, value) {
for (var i = arr.length - 1; i >= 0; i--) {
if (arr[i] === value) { return i; }
}
return -1;
}
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: lastIndexOf(searchValue, fromIndex) - PARTIAL (broken)
*
* Proves:
* 1. lastIndexOf IS present (typeof "function") - unlike indexOf.
* 2. DEVIATION: it is broken and ALWAYS returns -1, even for a value that
* is clearly in the array (spec: the last matching index).
* 3. The documented reverse for-loop workaround returns the correct last
* index and -1 when the value is absent.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
var liArr = ["a", "b", "a", "c"];
assert("typeof [].lastIndexOf is function", typeof liArr.lastIndexOf, "function");
assert("DEV lastIndexOf('a') returns -1 (spec: 2)", liArr.lastIndexOf("a"), "-1");
assert("DEV lastIndexOf('c') returns -1 (spec: 3)", liArr.lastIndexOf("c"), "-1");
assert("lastIndexOf('z') returns -1 (absent value)", liArr.lastIndexOf("z"), "-1");
function lastIndexOfPoly(arr, value) {
for (var i = arr.length - 1; i >= 0; i--) {
if (arr[i] === value) { return i; }
}
return -1;
}
assert("workaround lastIndexOf('a') is 2", lastIndexOfPoly(liArr, "a"), "2");
assert("workaround lastIndexOf('c') is 3", lastIndexOfPoly(liArr, "c"), "3");
assert("workaround lastIndexOf('z') is -1", lastIndexOfPoly(liArr, "z"), "-1");
</script>
forEach
(ES5) — ❌ Missing. Use a for loop or the polyfill.
for (var i = 0; i < arr.length; i++) {
var item = arr[i];
// process item
}
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: forEach(fn) - MISSING
*
* Proves:
* 1. Array.prototype.forEach is NOT available
* (typeof [].forEach is "undefined"; spec (ES5): a function).
* 2. Calling it throws.
* 3. The documented index for-loop visits every element in order.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var feArr = [1, 2, 3];
assert("DEV typeof [].forEach is undefined (spec ES5: function)", typeof feArr.forEach, "undefined");
assertThrows("DEV [].forEach(fn) throws (spec: visits each item)", function () { var a = [1]; return a.forEach(function () { return 1; }); });
var seen = [];
for (var i = 0; i < feArr.length; i++) {
seen.push(feArr[i]);
}
assert("workaround for-loop visits every item in order", seen.join(","), "1,2,3");
assert("workaround visited exactly length items", seen.length, "3");
</script>
map
(ES5) — ❌ Missing. Use a for loop or the polyfill.
var doubled = [];
for (var i = 0; i < arr.length; i++) {
doubled.push(arr[i] * 2);
}
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: map(fn) - MISSING
*
* Proves:
* 1. Array.prototype.map is NOT available
* (typeof [].map is "undefined"; spec (ES5): a function).
* 2. Calling it throws.
* 3. The documented for-loop + push workaround produces the mapped array
* and leaves the source untouched.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var mapArr = [1, 2, 3];
assert("DEV typeof [].map is undefined (spec ES5: function)", typeof mapArr.map, "undefined");
assertThrows("DEV [].map(fn) throws (spec: returns a mapped array)", function () { var a = [1]; return a.map(function (x) { return x; }); });
var doubled = [];
for (var i = 0; i < mapArr.length; i++) {
doubled.push(mapArr[i] * 2);
}
assert("workaround for-loop map gives 2,4,6", doubled.join(","), "2,4,6");
assert("workaround leaves the source array unchanged", mapArr.join(","), "1,2,3");
</script>
filter
(ES5) — ❌ Missing. Use a for loop or the polyfill.
var evens = [];
for (var i = 0; i < arr.length; i++) {
if (arr[i] % 2 === 0) { evens.push(arr[i]); }
}
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: filter(fn) - MISSING
*
* Proves:
* 1. Array.prototype.filter is NOT available
* (typeof [].filter is "undefined"; spec (ES5): a function).
* 2. Calling it throws.
* 3. The documented for-loop workaround collects only matching items,
* and yields an empty array when nothing matches.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var filArr = [1, 2, 3, 4];
assert("DEV typeof [].filter is undefined (spec ES5: function)", typeof filArr.filter, "undefined");
assertThrows("DEV [].filter(fn) throws (spec: returns matching items)", function () { var a = [1]; return a.filter(function (x) { return true; }); });
var evens = [];
for (var i = 0; i < filArr.length; i++) {
if (filArr[i] % 2 === 0) { evens.push(filArr[i]); }
}
assert("workaround for-loop filter gives 2,4", evens.join(","), "2,4");
var none = [];
for (var j = 0; j < filArr.length; j++) {
if (filArr[j] > 99) { none.push(filArr[j]); }
}
assert("workaround yields an empty array when nothing matches", none.length, "0");
</script>
reduce
(ES5) — ❌ Missing. Use a for loop or the polyfill.
var sum = 0;
for (var i = 0; i < arr.length; i++) {
sum += arr[i];
}
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: reduce(fn, initial) - MISSING
*
* Proves:
* 1. Array.prototype.reduce is NOT available
* (typeof [].reduce is "undefined"; spec (ES5): a function).
* 2. Calling it throws.
* 3. The documented accumulator for-loop sums the array, and an empty
* array leaves the initial value untouched.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var redArr = [1, 2, 3, 4];
assert("DEV typeof [].reduce is undefined (spec ES5: function)", typeof redArr.reduce, "undefined");
assertThrows("DEV [].reduce(fn,0) throws (spec: folds to a single value)", function () { var a = [1]; return a.reduce(function (acc, x) { return acc + x; }, 0); });
var sum = 0;
for (var i = 0; i < redArr.length; i++) {
sum += redArr[i];
}
assert("workaround accumulator loop sums to 10", sum, "10");
var emptySum = 7;
var emptyArr = [];
for (var j = 0; j < emptyArr.length; j++) {
emptySum += emptyArr[j];
}
assert("workaround keeps the initial value for an empty array", emptySum, "7");
</script>
reduceRight
(ES5) — ❌ Missing. Use a reverse for loop or the polyfill.
var sum = 0;
for (var i = arr.length - 1; i >= 0; i--) {
sum += arr[i];
}
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: reduceRight(fn, initial) - MISSING
*
* Proves:
* 1. Array.prototype.reduceRight is NOT available
* (typeof [].reduceRight is "undefined"; spec (ES5): a function).
* 2. Calling it throws.
* 3. The documented reverse for-loop folds right-to-left: summing gives the
* same total, and string concatenation proves the right-to-left order.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var rrArr = [1, 2, 3, 4];
assert("DEV typeof [].reduceRight is undefined (spec ES5: function)", typeof rrArr.reduceRight, "undefined");
assertThrows("DEV [].reduceRight(fn,0) throws (spec: folds right-to-left)", function () { var a = [1]; return a.reduceRight(function (acc, x) { return acc + x; }, 0); });
var sum = 0;
for (var i = rrArr.length - 1; i >= 0; i--) {
sum += rrArr[i];
}
assert("workaround reverse loop sums to 10", sum, "10");
var concatRight = "";
var letters = ["a", "b", "c"];
for (var j = letters.length - 1; j >= 0; j--) {
concatRight += letters[j];
}
assert("workaround folds right-to-left -> cba", concatRight, "cba");
</script>
some
(ES5) — ❌ Missing. Use a for loop or the polyfill.
var hasLarge = false;
for (var i = 0; i < arr.length; i++) {
if (arr[i] > 10) { hasLarge = true; break; }
}
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: some(fn) - MISSING
*
* Proves:
* 1. Array.prototype.some is NOT available
* (typeof [].some is "undefined"; spec (ES5): a function).
* 2. Calling it throws.
* 3. The documented early-break for-loop is true when at least one item
* matches and false when none do.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var someArr = [1, 20, 3];
assert("DEV typeof [].some is undefined (spec ES5: function)", typeof someArr.some, "undefined");
assertThrows("DEV [].some(fn) throws (spec: returns a boolean)", function () { var a = [1]; return a.some(function (x) { return true; }); });
var hasLarge = false;
for (var i = 0; i < someArr.length; i++) {
if (someArr[i] > 10) { hasLarge = true; break; }
}
assert("workaround some(>10) is true for [1,20,3]", hasLarge, "true");
var smallArr = [1, 2, 3];
var hasLarge2 = false;
for (var j = 0; j < smallArr.length; j++) {
if (smallArr[j] > 10) { hasLarge2 = true; break; }
}
assert("workaround some(>10) is false for [1,2,3]", hasLarge2, "false");
</script>
every
(ES5) — ❌ Missing. Use a for loop or the polyfill.
var allLarge = true;
for (var i = 0; i < arr.length; i++) {
if (arr[i] <= 10) { allLarge = false; break; }
}
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: every(fn) - MISSING
*
* Proves:
* 1. Array.prototype.every is NOT available
* (typeof [].every is "undefined"; spec (ES5): a function).
* 2. Calling it throws.
* 3. The documented early-break for-loop is true only when every item
* matches, and false as soon as one does not.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var everyArr = [20, 30, 40];
assert("DEV typeof [].every is undefined (spec ES5: function)", typeof everyArr.every, "undefined");
assertThrows("DEV [].every(fn) throws (spec: returns a boolean)", function () { var a = [1]; return a.every(function (x) { return true; }); });
var allLarge = true;
for (var i = 0; i < everyArr.length; i++) {
if (everyArr[i] <= 10) { allLarge = false; break; }
}
assert("workaround every(>10) is true for [20,30,40]", allLarge, "true");
var mixedArr = [20, 5, 40];
var allLarge2 = true;
for (var j = 0; j < mixedArr.length; j++) {
if (mixedArr[j] <= 10) { allLarge2 = false; break; }
}
assert("workaround every(>10) is false for [20,5,40]", allLarge2, "false");
</script>
find
(ES6) — ❌ Missing. Use a for loop or the polyfill.
var found = null;
for (var i = 0; i < arr.length; i++) {
if (arr[i].id === targetId) { found = arr[i]; break; }
}
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: find(fn) - MISSING
*
* Proves:
* 1. Array.prototype.find is NOT available
* (typeof [].find is "undefined"; spec (ES6): a function).
* 2. Calling it throws.
* 3. The documented early-break for-loop returns the matching item, and
* leaves the result null when nothing matches.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var fArr = [{ id: 1 }, { id: 7 }, { id: 9 }];
assert("DEV typeof [].find is undefined (spec ES6: function)", typeof fArr.find, "undefined");
assertThrows("DEV [].find(fn) throws (spec: returns the first match)", function () { return fArr.find(function (x) { return true; }); });
var found = null;
for (var i = 0; i < fArr.length; i++) {
if (fArr[i].id === 7) { found = fArr[i]; break; }
}
assert("workaround for-loop finds the matching item", found.id, "7");
var missing = null;
for (var j = 0; j < fArr.length; j++) {
if (fArr[j].id === 99) { missing = fArr[j]; break; }
}
assert("workaround leaves null when nothing matches", missing === null, "true");
</script>
findIndex
(ES6) — ❌ Missing. Use a for loop or the polyfill.
var idx = -1;
for (var i = 0; i < arr.length; i++) {
if (arr[i].id === targetId) { idx = i; break; }
}
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: findIndex(fn) - MISSING
*
* Proves:
* 1. Array.prototype.findIndex is NOT available
* (typeof [].findIndex is "undefined"; spec (ES6): a function).
* 2. Calling it throws.
* 3. The documented early-break for-loop yields the index of the match,
* and stays at -1 when nothing matches.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var fiArr = [{ id: 1 }, { id: 7 }, { id: 9 }];
assert("DEV typeof [].findIndex is undefined (spec ES6: function)", typeof fiArr.findIndex, "undefined");
assertThrows("DEV [].findIndex(fn) throws (spec: returns an index)", function () { return fiArr.findIndex(function (x) { return true; }); });
var idx = -1;
for (var i = 0; i < fiArr.length; i++) {
if (fiArr[i].id === 9) { idx = i; break; }
}
assert("workaround for-loop yields index 2", idx, "2");
var missIdx = -1;
for (var j = 0; j < fiArr.length; j++) {
if (fiArr[j].id === 99) { missIdx = j; break; }
}
assert("workaround stays -1 when nothing matches", missIdx, "-1");
</script>
includes
(ES6) — ❌ Missing. Use the polyfilled indexOf (indexOf(x) !== -1) or the polyfill.
function includes(arr, value) {
for (var i = 0; i < arr.length; i++) {
if (arr[i] === value) { return true; }
}
return false;
}
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: includes(value) - MISSING
*
* Proves:
* 1. Array.prototype.includes is NOT available
* (typeof [].includes is "undefined"; spec (ES6): a function).
* 2. Calling it throws.
* 3. The documented helper returns true for a present value and false for
* an absent one.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var incArr = [10, 20, 30];
assert("DEV typeof [].includes is undefined (spec ES6: function)", typeof incArr.includes, "undefined");
assertThrows("DEV [].includes(v) throws (spec: returns a boolean)", function () { return incArr.includes(10); });
function includes(a, value) {
for (var k = 0; k < a.length; k++) {
if (a[k] === value) { return true; }
}
return false;
}
assert("workaround helper is true for a present value", includes(incArr, 20), "true");
assert("workaround helper is false for an absent value", includes(incArr, 99), "false");
</script>
fill
(ES6) — ❌ Missing. Apply the polyfill, or assign in a loop.
var arr = [];
for (var i = 0; i < 5; i++) { arr.push(0); } // [0,0,0,0,0]
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: fill(value) - MISSING
*
* Proves:
* 1. Array.prototype.fill is NOT available
* (typeof [].fill is "undefined"; spec (ES6): a function).
* 2. Calling it throws.
* 3. The documented push loop builds [0,0,0,0,0].
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var fillArr = [];
assert("DEV typeof [].fill is undefined (spec ES6: function)", typeof fillArr.fill, "undefined");
assertThrows("DEV [].fill(0) throws (spec: fills the array in place)", function () { var a = [1, 2]; return a.fill(0); });
for (var i = 0; i < 5; i++) { fillArr.push(0); }
assert("workaround push loop yields 0,0,0,0,0", fillArr.join(","), "0,0,0,0,0");
assert("workaround push loop yields length 5", fillArr.length, "5");
</script>
copyWithin
(ES6) — ❌ Missing. Apply the polyfill.
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: copyWithin(target, start) - MISSING
*
* Proves:
* 1. Array.prototype.copyWithin is NOT available
* (typeof [].copyWithin is "undefined"; spec (ES6): a function).
* 2. Calling it throws, so the polyfill is required.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var cwArr = [10, 20, 30];
assert("DEV typeof [].copyWithin is undefined (spec ES6: function)", typeof cwArr.copyWithin, "undefined");
assertThrows("DEV [].copyWithin(0,1) throws (spec: shifts elements in place)", function () { var a = [1, 2, 3]; return a.copyWithin(0, 1); });
</script>
entries
(ES6) — ❌ Missing. Apply the polyfill, or iterate with an index for loop reading i and arr[i].
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: entries() - MISSING
*
* Proves:
* 1. Array.prototype.entries is NOT available
* (typeof [].entries is "undefined"; spec (ES6): a function).
* 2. Calling it throws.
* 3. The documented index for-loop reproduces index/value pairs.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var enArr = [10, 20, 30];
assert("DEV typeof [].entries is undefined (spec ES6: function)", typeof enArr.entries, "undefined");
assertThrows("DEV [].entries() throws (spec: returns an iterator)", function () { return enArr.entries(); });
var pairs = "";
for (var i = 0; i < enArr.length; i++) {
pairs += i + ":" + enArr[i] + ";";
}
assert("workaround index loop yields index/value pairs", pairs, "0:10;1:20;2:30;");
</script>
keys
(ES6) — ❌ Missing. Use a standard index for loop (for (var i = 0; i < arr.length; i++)).
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: keys() - MISSING
*
* Proves:
* 1. Array.prototype.keys is NOT available
* (typeof [].keys is "undefined"; spec (ES6): a function).
* 2. Calling it throws.
* 3. The documented index for-loop enumerates the indices.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var kArr = [10, 20, 30];
assert("DEV typeof [].keys is undefined (spec ES6: function)", typeof kArr.keys, "undefined");
assertThrows("DEV [].keys() throws (spec: returns an index iterator)", function () { return kArr.keys(); });
var ks = "";
for (var i = 0; i < kArr.length; i++) { ks += i; }
assert("workaround index loop enumerates 0,1,2", ks, "012");
</script>
values
(ES6) — ❌ Missing. Use a standard index for loop reading arr[i].
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: values() - MISSING
*
* Proves:
* 1. Array.prototype.values is NOT available
* (typeof [].values is "undefined"; spec (ES6): a function).
* 2. Calling it throws.
* 3. The documented index for-loop reads every value via arr[i].
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var vArr = [10, 20, 30];
assert("DEV typeof [].values is undefined (spec ES6: function)", typeof vArr.values, "undefined");
assertThrows("DEV [].values() throws (spec: returns a value iterator)", function () { return vArr.values(); });
var vs = "";
for (var i = 0; i < vArr.length; i++) { vs += vArr[i] + ","; }
assert("workaround index loop reads every value", vs, "10,20,30,");
</script>
at
(ES6) — ❌ Missing. Use arr[i], and arr[arr.length + i] for negative i.
var arr = [10, 20, 30];
arr[0]; // 10 (arr.at(0))
arr[arr.length - 1]; // 30 (arr.at(-1))
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: at(index) - MISSING
*
* Proves:
* 1. Array.prototype.at is NOT available
* (typeof [].at is "undefined"; spec (ES6+): a function).
* 2. Calling it throws.
* 3. The documented substitutes arr[0] and arr[arr.length - 1] return the
* values at(0) and at(-1) would.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var atArr = [10, 20, 30];
assert("DEV typeof [].at is undefined (spec: function)", typeof atArr.at, "undefined");
assertThrows("DEV [].at(0) throws (spec: returns the item at an index)", function () { return atArr.at(0); });
assert("workaround arr[0] equals at(0)", atArr[0], "10");
assert("workaround arr[arr.length - 1] equals at(-1)", atArr[atArr.length - 1], "30");
</script>
flat
(ES6) — ❌ Missing. Concatenate nested arrays manually in a loop.
function flatten(arr) {
var result = [];
for (var i = 0; i < arr.length; i++) {
if (typeof arr[i] === "object" && arr[i].length !== undefined) {
for (var j = 0; j < arr[i].length; j++) { result.push(arr[i][j]); }
} else {
result.push(arr[i]);
}
}
return result;
}
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: flat() - MISSING
*
* Proves:
* 1. Array.prototype.flat is NOT available
* (typeof [].flat is "undefined"; spec (ES6+): a function).
* 2. Calling it throws.
* 3. The documented flatten() helper turns [1,[2,3],4] into [1,2,3,4].
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var nested = [1, [2, 3], 4];
assert("DEV typeof [].flat is undefined (spec: function)", typeof nested.flat, "undefined");
assertThrows("DEV [].flat() throws (spec: flattens nested arrays)", function () { return nested.flat(); });
function flatten(a) {
var result = [];
for (var i = 0; i < a.length; i++) {
if (typeof a[i] === "object" && a[i].length !== undefined) {
for (var j = 0; j < a[i].length; j++) { result.push(a[i][j]); }
} else {
result.push(a[i]);
}
}
return result;
}
assert("workaround flatten() yields 1,2,3,4", flatten(nested).join(","), "1,2,3,4");
assert("workaround flatten() yields length 4", flatten(nested).length, "4");
</script>
flatMap
(ES6) — ❌ Missing. Build the result with a for loop and push.
var result = [];
for (var i = 0; i < arr.length; i++) {
var mapped = transform(arr[i]); // returns an array
for (var j = 0; j < mapped.length; j++) { result.push(mapped[j]); }
}
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: flatMap(fn) - MISSING
*
* Proves:
* 1. Array.prototype.flatMap is NOT available
* (typeof [].flatMap is "undefined"; spec (ES6+): a function).
* 2. Calling it throws.
* 3. The documented nested for-loop with push flattens the mapped arrays.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var fmArr = [10, 20, 30];
assert("DEV typeof [].flatMap is undefined (spec: function)", typeof fmArr.flatMap, "undefined");
assertThrows("DEV [].flatMap(fn) throws (spec: maps then flattens)", function () { return fmArr.flatMap(function (x) { return [x]; }); });
function transform(x) { return [x, x * 2]; }
var fmResult = [];
for (var i = 0; i < fmArr.length; i++) {
var mapped = transform(fmArr[i]);
for (var j = 0; j < mapped.length; j++) { fmResult.push(mapped[j]); }
}
assert("workaround nested loop flattens the mapped arrays", fmResult.join(","), "10,20,20,40,30,60");
</script>
findLast
(ES6) — ❌ Missing. Iterate from the end with a for loop.
var found = null;
for (var i = arr.length - 1; i >= 0; i--) {
if (predicate(arr[i])) { found = arr[i]; break; }
}
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: findLast(fn) - MISSING
*
* Proves:
* 1. Array.prototype.findLast is NOT available
* (typeof [].findLast is "undefined"; spec (ES6+): a function).
* 2. Calling it throws.
* 3. The documented reverse for-loop returns the LAST match, and leaves
* the result null when nothing matches.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var flArr = [10, 20, 30];
assert("DEV typeof [].findLast is undefined (spec: function)", typeof flArr.findLast, "undefined");
assertThrows("DEV [].findLast(fn) throws (spec: returns the last match)", function () { return flArr.findLast(function (x) { return true; }); });
var lastFound = null;
for (var i = flArr.length - 1; i >= 0; i--) {
if (flArr[i] >= 20) { lastFound = flArr[i]; break; }
}
assert("workaround reverse loop returns the LAST match", lastFound, "30");
var lastMiss = null;
for (var j = flArr.length - 1; j >= 0; j--) {
if (flArr[j] > 999) { lastMiss = flArr[j]; break; }
}
assert("workaround leaves null when nothing matches", lastMiss === null, "true");
</script>
Array.isArray
(ES5) — ❌ Missing. Apply the polyfill.
function isArray(value) {
return Object.prototype.toString.call(value) === "[object Array]";
}
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: Array.isArray(value) - MISSING
*
* Proves:
* 1. Array.isArray is NOT available
* (typeof Array.isArray is "undefined"; spec (ES5): a function).
* 2. Calling it throws.
* 3. The documented Object.prototype.toString helper is true for arrays and
* false for plain objects and strings.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
assert("DEV typeof Array.isArray is undefined (spec ES5: function)", typeof Array.isArray, "undefined");
assertThrows("DEV Array.isArray([]) throws (spec: returns a boolean)", function () { return Array.isArray([]); });
function isArray(value) {
return Object.prototype.toString.call(value) === "[object Array]";
}
assert("workaround is true for an array", isArray([1, 2]), "true");
assert("workaround is false for a plain object", isArray({ a: 1 }), "false");
assert("workaround is false for a string", isArray("x"), "false");
</script>
Array.of
(ES6) — ❌ Missing. Apply the polyfill, or build an array literal directly.
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: Array.of(...) - MISSING
*
* Proves:
* 1. Array.of is NOT available
* (typeof Array.of is "undefined"; spec (ES6): a function).
* 2. Calling it throws.
* 3. The documented array literal builds the same array.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
assert("DEV typeof Array.of is undefined (spec ES6: function)", typeof Array.of, "undefined");
assertThrows("DEV Array.of(1,2,3) throws (spec: builds an array of the args)", function () { return Array.of(1, 2, 3); });
var literal = [1, 2, 3];
assert("workaround array literal builds the same array", literal.join(","), "1,2,3");
</script>
Array.from
(ES6) — ❌ Missing. Build the array with a for loop over the source.
var arr = [];
for (var i = 0; i < source.length; i++) { arr.push(source[i]); }
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: Array.from(source) - MISSING
*
* Proves:
* 1. Array.from is NOT available
* (typeof Array.from is "undefined"; spec (ES6): a function).
* 2. Calling it throws.
* 3. The documented push loop copies the source into a NEW array.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
var got;
try { got = String(actual); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
assert("DEV typeof Array.from is undefined (spec ES6: function)", typeof Array.from, "undefined");
assertThrows("DEV Array.from([1,2]) throws (spec: builds an array from a source)", function () { return Array.from([1, 2]); });
var source = [7, 8, 9];
var copy = [];
for (var i = 0; i < source.length; i++) { copy.push(source[i]); }
assert("workaround push loop copies the source", copy.join(","), "7,8,9");
assert("workaround yields a NEW array, not the source", copy === source, "false");
</script>