Each member below is tagged with the ECMAScript edition that standardized it: (ES3), (ES5), or (ES6). Methods that need a polyfill link to Polyfills.

The String() constructor

(ES3) — ✅ Works. String(value) is the native constructor called as a conversion function: it converts any value to its string representation. Called with no argument, String() returns "". In SFMC SSJS it has a critical extra use case: converting CLR/.NET objects — most importantly the .content of a Script.Util.HttpRequest response — into real JavaScript strings.

String(42);       // "42"
String(true);     // "true"
String();         // ""

Converting CLR HTTP response content

resp.content is a .NET object, not a JS string — it cannot be passed directly to Platform.Function.ParseJSON(). Convert it first:

var req = new Script.Util.HttpRequest("https://api.example.com/data");
req.method = "GET";
var resp = req.send();

// resp.content is a CLR object — convert before parsing
var bodyStr = String(resp.content);                       // CLR → JS string
var data    = Platform.Function.ParseJSON(bodyStr + "");  // JS string → object

String() vs Stringify()

  • String(value) — converts primitives and CLR/engine values to a plain JS string. It does not render a plain object: String({}) throws Object reference not set to an instance of an object.
  • Stringify(value) — serializes a JavaScript value to a JSON string. This is the reliable way to render an object.
var obj = { a: 1, b: 2 };
String(obj);      // THROWS "Object reference not set to an instance of an object."
("" + obj);       // ""                  — the empty string, NOT "[object Object]"
Stringify(obj);   // '{"a":1,"b":2}'     — JSON serialization

The same throw applies to a caught plain object — do not call String(e) on one; see Error().

For engine/CLR values, prefer ("" + value) over String(value). Both produce a real JS string that satisfies === and supports string methods, but String(value) throws Object reference not set to an instance of an object. on .NET-null-backed CLR properties (resp.contentType, resp.encoding, resp.headers) and on plain objects, where ("" + value) safely yields "".

Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: The String() constructor
 *
 * Proves:
 *   1. String(value) converts primitives to their string representation:
 *      String(42) -> "42", String(true) -> "true".
 *   2. String() with no argument returns "".
 *   3. Stringify(value) produces JSON, unlike String(value).
 *
 * NOT ASSERTED HERE (settled by an isolated probe, not re-run in this bundle):
 *   - Plain-object coercion. Probe clr-coerce-3 (2026-08-08, recorded in the
 *     verification DB entry for String) settled it: String({}) THROWS "Object
 *     reference not set to an instance of an object." — catchable, and it
 *     does NOT abort the page — while "" + {} yields the EMPTY STRING, not
 *     the spec's "[object Object]". String([]) and "" + [] are also both "".
 *     The page and ssjs-data now state this. No assertion for it has been
 *     observed green in THIS bundle yet, so none is added here.
 *   - String(resp.content) converting CLR HTTP response content. That needs
 *     a live outbound HTTP call, which is non-deterministic and risks the
 *     CloudPage timeout; it is covered on the Script.Util.HttpRequest page.
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === expected ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}

/* 1. Primitive conversion. */
assert("String(42) is '42'", function () { return String(42); }, "42");
assert("String(true) is 'true'", function () { return String(true); }, "true");

/* 2. No argument returns the empty string. */
assert("String() is ''", function () { return String(); }, "");

/* 3. Stringify() serializes to JSON instead. */
var obj = { a: 1, b: 2 };
assert("Stringify({a:1,b:2}) is JSON", function () { return String(Stringify(obj)); }, '{"a":1,"b":2}');
</script>

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
length ES3 ✅ Works  
charAt(index) ES3 ⚠️ Partial Out-of-range index returns the last char, not ""
charCodeAt(index) ES3 ✅ Works  
indexOf(search, fromIndex) ES3 ✅ Works  
lastIndexOf(search, fromIndex) ES3 ✅ Works  
toUpperCase() ES3 ✅ Works  
toLowerCase() ES3 ✅ Works  
toLocaleLowerCase() ES3 ✅ Works  
substring(start, end) ES3 ✅ Works  
slice(start, end) ES3 ✅ Works  
concat(...strings) ES3 ✅ Works  
replace(pattern, replacement) ES3 ✅ Works  
localeCompare(other) ES3 ✅ Works  
match(regexp) ES3 ⚠️ Partial Returns [] (not null) on no match; no .index
search(regexp) ES3 ⚠️ Partial Returns 0 (not -1) on no match; unreliable — see Polyfills
split(separator, limit) ES3 ⚠️ Partial Empty-separator form does not split into chars — see Polyfills
trim() ES5 ❌ Missing See Polyfills
substr(start, length) ES3 ❌ Missing Throws at runtime — use substring/slice or polyfill
startsWith(prefix) ES6 ❌ Missing Use indexOf(prefix) === 0 or polyfill
endsWith(suffix) ES6 ❌ Missing Use lastIndexOf check or polyfill
includes(substr) ES6 ❌ Missing Use indexOf(substr) !== -1
trimStart() ES6 ❌ Missing Use a /^\s+/ replace
trimEnd() ES6 ❌ Missing Use a /\s+$/ replace
padStart(targetLen, pad) ES6 ❌ Missing Prepend pad characters in a loop
padEnd(targetLen, pad) ES6 ❌ Missing Append pad characters in a loop
repeat(count) ES6 ❌ Missing Concatenate in a loop
codePointAt(index) ES6 ❌ Missing Use charCodeAt for BMP characters

length

(ES3) — ✅ Works. The number of UTF-16 code units in the string.

"Hello".length;   // 5
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: length
 *
 * Proves:
 *   1. "Hello".length is 5 — the number of UTF-16 code units.
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === expected ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}

assert("'Hello'.length is 5", function () { return String("Hello".length); }, "5");
</script>

charAt

(ES3) — ⚠️ Partial. Returns the character at the given index. In-range indices behave normally, but out-of-range indices are broken: instead of the spec-mandated empty string "", SFMC returns the last character of the string. Guard the index against .length before calling.

"Hello".charAt(0);    // "H"
"Hello"[0];           // "H"  (bracket access works in range)

"Hello".charAt(99);   // "o"  — ❌ SFMC returns the last char, not ""
"Hello".charAt(5);    // "o"  — ❌ same bug (spec says "")
"Hello"[99];          // throws "Index was outside the bounds of the array"
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: charAt
 *
 * Proves:
 *   1. In-range charAt() returns the character at that index.
 *   2. Bracket access works for an in-range index.
 *   3. DEVIATION marked "DEV": an out-of-range index returns the LAST
 *      character of the string (spec: the empty string ""), both for an
 *      index far past the end and for index === length.
 *   4. Bracket access with an out-of-range index throws instead.
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === 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");
}

/* 1. + 2. In-range access. */
assert("'Hello'.charAt(0) is 'H'", function () { return String("Hello".charAt(0)); }, "H");
assert("'Hello'[0] is 'H'", function () { return String("Hello"[0]); }, "H");

/* 3. DEVIATION — out of range returns the last character. */
assert("DEV 'Hello'.charAt(99) is 'o' (spec: '')", function () { return String("Hello".charAt(99)); }, "o");
assert("DEV 'Hello'.charAt(5) is 'o' (spec: '')", function () { return String("Hello".charAt(5)); }, "o");

/* 4. Bracket access is not forgiving. */
assertThrows("'Hello'[99] throws", function () { return "Hello"[99]; });
</script>

charCodeAt

(ES3) — ✅ Works. Returns the UTF-16 code unit at the given index.

"Hello".charCodeAt(0);   // 72
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: charCodeAt
 *
 * Proves:
 *   1. "Hello".charCodeAt(0) is 72 — the UTF-16 code unit at that index.
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === expected ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}

assert("'Hello'.charCodeAt(0) is 72", function () { return String("Hello".charCodeAt(0)); }, "72");
</script>

indexOf

(ES3) — ✅ Works. Returns the index of the first occurrence, or -1.

"Hello World".indexOf("World");   // 6
"Hello World".indexOf("o", 5);    // 7
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: indexOf
 *
 * Proves:
 *   1. indexOf(search) returns the index of the first occurrence.
 *   2. indexOf(search, fromIndex) starts searching at fromIndex.
 *   3. A missing needle returns -1.
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === expected ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}

assert("'Hello World'.indexOf('World') is 6", function () { return String("Hello World".indexOf("World")); }, "6");
assert("'Hello World'.indexOf('o', 5) is 7", function () { return String("Hello World".indexOf("o", 5)); }, "7");
assert("no match returns -1", function () { return String("Hello World".indexOf("zzz")); }, "-1");
</script>

lastIndexOf

(ES3) — ✅ Works. Returns the index of the last occurrence, or -1.

"Hello World Hello".lastIndexOf("Hello");   // 12
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: lastIndexOf
 *
 * Proves:
 *   1. lastIndexOf(search) returns the index of the LAST occurrence.
 *   2. A missing needle returns -1.
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === expected ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}

assert("'Hello World Hello'.lastIndexOf('Hello') is 12", function () { return String("Hello World Hello".lastIndexOf("Hello")); }, "12");
assert("no match returns -1", function () { return String("Hello World Hello".lastIndexOf("zzz")); }, "-1");
</script>

toUpperCase

(ES3) — ✅ Works.

"Hello".toUpperCase();   // "HELLO"
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: toUpperCase
 *
 * Proves:
 *   1. "Hello".toUpperCase() is "HELLO".
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === expected ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}

assert("'Hello'.toUpperCase() is 'HELLO'", function () { return String("Hello".toUpperCase()); }, "HELLO");
</script>

toLowerCase

(ES3) — ✅ Works.

"Hello".toLowerCase();   // "hello"
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: toLowerCase
 *
 * Proves:
 *   1. "Hello".toLowerCase() is "hello".
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === expected ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}

assert("'Hello'.toLowerCase() is 'hello'", function () { return String("Hello".toLowerCase()); }, "hello");
</script>

toLocaleLowerCase

(ES3) — ✅ Works. Locale-aware lowercase.

"Hello".toLocaleLowerCase();   // "hello"
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: toLocaleLowerCase
 *
 * Proves:
 *   1. "Hello".toLocaleLowerCase() is "hello" — the locale-aware lowercase
 *      method exists and works.
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === expected ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}

assert("'Hello'.toLocaleLowerCase() is 'hello'", function () { return String("Hello".toLocaleLowerCase()); }, "hello");
</script>

substring

(ES3) — ✅ Works. Returns the part of the string between two indices.

"Hello World".substring(6, 11);   // "World"
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: substring
 *
 * Proves:
 *   1. "Hello World".substring(6, 11) is "World" — the part between the two
 *      indices.
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === expected ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}

assert("'Hello World'.substring(6, 11) is 'World'", function () { return String("Hello World".substring(6, 11)); }, "World");
</script>

slice

(ES3) — ✅ Works. Like substring, but supports negative indices.

"Hello World".slice(0, 5);   // "Hello"
"Hello World".slice(-5);     // "World"
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: slice
 *
 * Proves:
 *   1. slice(start, end) behaves like substring.
 *   2. slice() supports NEGATIVE indices, which is what distinguishes it
 *      from substring: "Hello World".slice(-5) is "World".
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === expected ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}

assert("'Hello World'.slice(0, 5) is 'Hello'", function () { return String("Hello World".slice(0, 5)); }, "Hello");
assert("'Hello World'.slice(-5) is 'World'", function () { return String("Hello World".slice(-5)); }, "World");
</script>

concat

(ES3) — ✅ Works. Concatenates strings. + is usually preferred.

"Hello".concat(" ", "World");   // "Hello World"
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: concat
 *
 * Proves:
 *   1. concat() joins several strings: "Hello".concat(" ", "World").
 *   2. The + operator produces the same result, which is why the chapter
 *      recommends it.
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === expected ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}

assert("'Hello'.concat(' ', 'World') is 'Hello World'", function () { return String("Hello".concat(" ", "World")); }, "Hello World");
assert("+ produces the same result", function () { return String("Hello" + " " + "World"); }, "Hello World");
</script>

replace

(ES3) — ✅ Works. Replaces matches of a string or RegExp. Use the /g flag to replace all.

"aabbcc".replace("b", "X");    // "aaXbcc"
"aabbcc".replace(/b/g, "X");   // "aaXXcc"
"hello world".replace(/\b\w/g, function (c) { return c.toUpperCase(); });   // "Hello World"
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: replace
 *
 * Proves:
 *   1. A string pattern replaces only the FIRST occurrence:
 *      "aabbcc".replace("b", "X") -> "aaXbcc".
 *   2. A RegExp with the /g flag replaces ALL occurrences:
 *      "aabbcc".replace(/b/g, "X") -> "aaXXcc".
 *   3. A function replacement receives the match and its return value is
 *      substituted: "hello world".replace(/\b\w/g, upper) -> "Hello World".
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === expected ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}

/* 1. String pattern — first match only. */
assert("'aabbcc'.replace('b', 'X') is 'aaXbcc'", function () { return String("aabbcc".replace("b", "X")); }, "aaXbcc");

/* 2. RegExp with /g — all matches. */
assert("'aabbcc'.replace(/b/g, 'X') is 'aaXXcc'", function () { return String("aabbcc".replace(/b/g, "X")); }, "aaXXcc");

/* 3. Function replacement. */
assert("function replacement capitalizes each word", function () {
    return String("hello world".replace(/\b\w/g, function (c) { return c.toUpperCase(); }));
}, "Hello World");
</script>

localeCompare

(ES3) — ✅ Works. Compares two strings in the current locale.

"apple".localeCompare("banana");   // negative
"apple".localeCompare("apple");    // 0
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: localeCompare
 *
 * Proves:
 *   1. "apple".localeCompare("banana") is NEGATIVE (asserted as a sign test,
 *      because the spec only fixes the sign, not the magnitude).
 *   2. "apple".localeCompare("apple") is 0.
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === expected ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}

assert("'apple'.localeCompare('banana') is negative", function () {
    return "apple".localeCompare("banana") < 0 ? "negative" : "not-negative";
}, "negative");
assert("'apple'.localeCompare('apple') is 0", function () { return String("apple".localeCompare("apple")); }, "0");
</script>

match

(ES3) — ⚠️ Partial. In SFMC, String.match returns an empty array [] (not null) when there is no match, and matched results do not carry an .index property.

var str = "Call 555-1234 or 555-5678";
str.match(/\d{3}-\d{4}/g);   // ["555-1234", "555-5678"]
str.match(/zzz/);            // [] (empty array in SFMC, not null)
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: match
 *
 * Proves:
 *   1. A /g match returns every match: "Call 555-1234 or 555-5678"
 *      matched with /\d{3}-\d{4}/g yields ["555-1234", "555-5678"].
 *   2. DEVIATION marked "DEV": a no-match returns an EMPTY ARRAY (spec:
 *      null) — asserted via its length, and by proving the result is not
 *      null.
 *   3. DEVIATION marked "DEV": the result carries NO .index property
 *      (spec: a non-global match result has .index).
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === expected ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}

var str = "Call 555-1234 or 555-5678";

/* 1. Global match returns all matches. */
assert("match(/\\d{3}-\\d{4}/g) finds 2 matches", function () { return String(str.match(/\d{3}-\d{4}/g).length); }, "2");
assert("match(/\\d{3}-\\d{4}/g) joined", function () { return String(str.match(/\d{3}-\d{4}/g).join("|")); }, "555-1234|555-5678");

/* 2. DEVIATION — no match yields [] rather than null. */
assert("DEV no-match result is not null (spec: null)", function () { return str.match(/zzz/) === null ? "null" : "not-null"; }, "not-null");
assert("DEV no-match result is an empty array (spec: null)", function () { return String(str.match(/zzz/).length); }, "0");

/* 3. DEVIATION — no .index on the result. */
assert("DEV match result has no .index (spec: number)", function () { return String(typeof str.match(/555-1234/).index); }, "undefined");
</script>

(ES3) — ⚠️ Partial. Unreliable in SFMC: returns 0 instead of -1 for a no-match, and some real matches return the wrong index. Use match or RegExp.test to detect a match, or apply the polyfill.

"abc123".search(/\d/);   // unreliable — prefer match() or RegExp.test()
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: search
 *
 * Proves:
 *   1. DEVIATION marked "DEV": a no-match returns 0 (spec: -1).
 *   2. The documented workarounds detect a match reliably where search()
 *      cannot: String.match() and RegExp.test().
 *
 * NOT ASSERTABLE (documented on the page, deliberately not asserted here):
 *   - "some real matches return the wrong index". The page states the
 *     returned index is unreliable without pinning it to a specific wrong
 *     value, so there is no deterministic expected value to assert. The
 *     actionable part — do not trust the index, use match()/test() instead —
 *     IS asserted, in point 2.
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === expected ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}

/* 1. DEVIATION — no match reports 0, which is indistinguishable from a
   match at index 0, so search() cannot be used to detect a match. */
assert("DEV 'abc'.search(/\\d/) is 0 for a NO match (spec: -1)", function () { return String("abc".search(/\d/)); }, "0");

/* 2. The recommended replacements. */
assert("workaround: match() finds the digit", function () { return String("abc123".match(/\d/).length > 0 ? "found" : "not-found"); }, "found");
assert("workaround: match() reports no digit", function () { return String("abc".match(/\d/).length > 0 ? "found" : "not-found"); }, "not-found");
assert("workaround: RegExp.test() finds the digit", function () { return /\d/.test("abc123") ? "true" : "false"; }, "true");
assert("workaround: RegExp.test() reports no digit", function () { return /\d/.test("abc") ? "true" : "false"; }, "false");
</script>

split

(ES3) — ⚠️ Partial. The empty-separator form str.split("") does not split into characters in SFMC — it returns the whole string as a single element. Loop with charAt, or apply the polyfill.

"a,b,c".split(",");      // ["a", "b", "c"]
"a  b  c".split(/\s+/);  // ["a", "b", "c"]

// "hello".split("") — ❌ does NOT split into chars in SFMC. Loop instead:
var chars = [];
var s = "hello";
for (var i = 0; i < s.length; i++) { chars.push(s.charAt(i)); }
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: split
 *
 * Proves:
 *   1. A string separator splits normally: "a,b,c".split(",").
 *   2. A RegExp separator splits normally: "a  b  c".split(/\s+/).
 *   3. DEVIATION marked "DEV": the empty-separator form "hello".split("")
 *      does NOT split into characters (spec: one element per character) —
 *      it returns the whole string as a single element.
 *   4. The documented workaround: a charAt() loop does produce one element
 *      per character.
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === expected ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}

/* 1. String separator. */
assert("'a,b,c'.split(',') has 3 parts", function () { return String("a,b,c".split(",").length); }, "3");
assert("'a,b,c'.split(',') joined", function () { return String("a,b,c".split(",").join("|")); }, "a|b|c");

/* 2. RegExp separator. */
assert("'a  b  c'.split(/\\s+/) has 3 parts", function () { return String("a  b  c".split(/\s+/).length); }, "3");
assert("'a  b  c'.split(/\\s+/) joined", function () { return String("a  b  c".split(/\s+/).join("|")); }, "a|b|c");

/* 3. DEVIATION — empty separator does not split into characters. */
assert("DEV 'hello'.split('') has 1 element (spec: 5)", function () { return String("hello".split("").length); }, "1");
assert("DEV 'hello'.split('')[0] is the whole string (spec: 'h')", function () { return String("hello".split("")[0]); }, "hello");

/* 4. The documented charAt() loop workaround. */
assert("workaround: charAt loop yields 5 chars", function () {
    var chars = [], s = "hello";
    for (var i = 0; i < s.length; i++) { chars.push(s.charAt(i)); }
    return String(chars.length);
}, "5");
assert("workaround: charAt loop joined", function () {
    var chars = [], s = "hello";
    for (var i = 0; i < s.length; i++) { chars.push(s.charAt(i)); }
    return String(chars.join("|"));
}, "h|e|l|l|o");
</script>

trim

(ES5) — ❌ Missing. Apply the polyfill. There is no Platform.Function.Trim in SSJS — reading it reports clrmethodinfo (a phantom member), but calling it throws Unable to retrieve security descriptor for this frame.

function trim(str) {
    return String(str).replace(/^\s+/, "").replace(/\s+$/, "");
}
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: trim
 *
 * Proves:
 *   1. String.prototype.trim is MISSING: reading it yields undefined and
 *      calling it throws.
 *   2. The documented polyfill (two replace() calls) strips leading and
 *      trailing whitespace.
 *   3. There is NO Platform.Function.Trim: reading it reports the phantom
 *      "clrmethodinfo" type, but calling it throws.
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === 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");
}

/* 1. The member does not exist. */
assert("typeof '  x  '.trim is undefined", function () { return String(typeof "  x  ".trim); }, "undefined");
assertThrows("'  x  '.trim() throws", function () { return "  x  ".trim(); });

/* 2. The documented polyfill. */
function trim(str) {
    return String(str).replace(/^\s+/, "").replace(/\s+$/, "");
}
assert("polyfill trim('  hi  ') is 'hi'", function () { return String(trim("  hi  ")); }, "hi");
assert("polyfill trim leaves inner spaces", function () { return String(trim("  a b  ")); }, "a b");

/* 3. Platform.Function.Trim does not exist. "clrmethodinfo" is reported for
   any member name under Platform.Function, existing or not — it is not
   evidence that the member is real. */
assert("typeof Platform.Function.Trim is the phantom clrmethodinfo", function () { return String(typeof Platform.Function.Trim); }, "clrmethodinfo");
assertThrows("Platform.Function.Trim('  hi  ') throws", function () { return Platform.Function.Trim("  hi  "); });
</script>

substr

(ES3) — ❌ Missing. String.prototype.substr throws at runtime in SFMC. Use substring or slice, or apply the polyfill.

"Hello World".substring(6, 11);   // "World"  (instead of substr(6, 5))
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: substr
 *
 * Proves:
 *   1. String.prototype.substr is MISSING: calling it throws at runtime.
 *   2. The documented replacement substring(6, 11) produces "World", the
 *      value substr(6, 5) would have produced.
 *   3. slice(6, 11) produces the same replacement value.
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === 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");
}

/* 1. Calling substr throws. */
assertThrows("'Hello World'.substr(6, 5) throws", function () { return "Hello World".substr(6, 5); });

/* 2. + 3. The documented replacements. */
assert("substring(6, 11) is 'World'", function () { return String("Hello World".substring(6, 11)); }, "World");
assert("slice(6, 11) is 'World'", function () { return String("Hello World".slice(6, 11)); }, "World");
</script>

startsWith

(ES6) — ❌ Missing. Use indexOf(prefix) === 0 or the polyfill.

function startsWith(str, prefix) {
    return str.indexOf(prefix) === 0;
}
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: startsWith
 *
 * Proves:
 *   1. String.prototype.startsWith is MISSING: reading it yields undefined
 *      and calling it throws.
 *   2. The documented workaround indexOf(prefix) === 0 returns true for a
 *      real prefix and false for a non-prefix (including a substring that
 *      occurs later in the string).
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === 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");
}

/* 1. The member does not exist. */
assert("typeof 'Hello'.startsWith is undefined", function () { return String(typeof "Hello".startsWith); }, "undefined");
assertThrows("'Hello'.startsWith('He') throws", function () { return "Hello".startsWith("He"); });

/* 2. The documented workaround. */
function startsWith(str, prefix) {
    return str.indexOf(prefix) === 0;
}
assert("workaround startsWith('Hello','He') is true", function () { return startsWith("Hello", "He") ? "true" : "false"; }, "true");
assert("workaround startsWith('Hello','llo') is false", function () { return startsWith("Hello", "llo") ? "true" : "false"; }, "false");
assert("workaround startsWith('Hello','zzz') is false", function () { return startsWith("Hello", "zzz") ? "true" : "false"; }, "false");
</script>

endsWith

(ES6) — ❌ Missing. Use a lastIndexOf check or the polyfill.

function endsWith(str, suffix) {
    return str.lastIndexOf(suffix) === str.length - suffix.length;
}
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: endsWith
 *
 * Proves:
 *   1. String.prototype.endsWith is MISSING: reading it yields undefined
 *      and calling it throws.
 *   2. The documented lastIndexOf workaround returns true for a real suffix
 *      and false for a non-suffix.
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === 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");
}

/* 1. The member does not exist. */
assert("typeof 'Hello'.endsWith is undefined", function () { return String(typeof "Hello".endsWith); }, "undefined");
assertThrows("'Hello'.endsWith('llo') throws", function () { return "Hello".endsWith("llo"); });

/* 2. The documented workaround. */
function endsWith(str, suffix) {
    return str.lastIndexOf(suffix) === str.length - suffix.length;
}
assert("workaround endsWith('Hello','llo') is true", function () { return endsWith("Hello", "llo") ? "true" : "false"; }, "true");
assert("workaround endsWith('Hello','He') is false", function () { return endsWith("Hello", "He") ? "true" : "false"; }, "false");
</script>

includes

(ES6) — ❌ Missing. Use indexOf(substr) !== -1.

function includes(str, sub) {
    return str.indexOf(sub) !== -1;
}
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: includes
 *
 * Proves:
 *   1. String.prototype.includes is MISSING: reading it yields undefined
 *      and calling it throws.
 *   2. The documented workaround indexOf(sub) !== -1 is true for a present
 *      substring and false for an absent one.
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === 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");
}

/* 1. The member does not exist. */
assert("typeof 'Hello'.includes is undefined", function () { return String(typeof "Hello".includes); }, "undefined");
assertThrows("'Hello'.includes('ell') throws", function () { return "Hello".includes("ell"); });

/* 2. The documented workaround. */
function includes(str, sub) {
    return str.indexOf(sub) !== -1;
}
assert("workaround includes('Hello','ell') is true", function () { return includes("Hello", "ell") ? "true" : "false"; }, "true");
assert("workaround includes('Hello','zzz') is false", function () { return includes("Hello", "zzz") ? "true" : "false"; }, "false");
</script>

trimStart

(ES6) — ❌ Missing. Use a /^\s+/ replace.

function trimStart(str) {
    return String(str).replace(/^\s+/, "");
}
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: trimStart
 *
 * Proves:
 *   1. String.prototype.trimStart is MISSING: reading it yields undefined
 *      and calling it throws.
 *   2. The documented /^\s+/ replace workaround removes only LEADING
 *      whitespace and leaves trailing whitespace intact.
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === 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");
}

/* 1. The member does not exist. */
assert("typeof '  x  '.trimStart is undefined", function () { return String(typeof "  x  ".trimStart); }, "undefined");
assertThrows("'  x  '.trimStart() throws", function () { return "  x  ".trimStart(); });

/* 2. The documented workaround. */
function trimStart(str) {
    return String(str).replace(/^\s+/, "");
}
assert("workaround trimStart('  hi  ') keeps trailing spaces", function () { return String(trimStart("  hi  ")); }, "hi  ");
assert("workaround trimStart('  hi  ').length is 4", function () { return String(trimStart("  hi  ").length); }, "4");
</script>

trimEnd

(ES6) — ❌ Missing. Use a /\s+$/ replace.

function trimEnd(str) {
    return String(str).replace(/\s+$/, "");
}
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: trimEnd
 *
 * Proves:
 *   1. String.prototype.trimEnd is MISSING: reading it yields undefined and
 *      calling it throws.
 *   2. The documented /\s+$/ replace workaround removes only TRAILING
 *      whitespace and leaves leading whitespace intact.
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === 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");
}

/* 1. The member does not exist. */
assert("typeof '  x  '.trimEnd is undefined", function () { return String(typeof "  x  ".trimEnd); }, "undefined");
assertThrows("'  x  '.trimEnd() throws", function () { return "  x  ".trimEnd(); });

/* 2. The documented workaround. */
function trimEnd(str) {
    return String(str).replace(/\s+$/, "");
}
assert("workaround trimEnd('  hi  ') keeps leading spaces", function () { return String(trimEnd("  hi  ")); }, "  hi");
assert("workaround trimEnd('  hi  ').length is 4", function () { return String(trimEnd("  hi  ").length); }, "4");
</script>

padStart

(ES6) — ❌ Missing. Prepend pad characters in a loop.

function padStart(str, targetLen, padChar) {
    str = String(str);
    padChar = padChar || " ";
    while (str.length < targetLen) { str = padChar + str; }
    return str;
}
padStart("7", 3, "0");   // "007"
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: padStart
 *
 * Proves:
 *   1. String.prototype.padStart is MISSING: reading it yields undefined
 *      and calling it throws.
 *   2. The documented loop workaround pads on the LEFT:
 *      padStart("7", 3, "0") -> "007".
 *   3. The workaround leaves an already-long-enough string untouched.
 *   4. Its padChar defaults to a space.
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === 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");
}

/* 1. The member does not exist. */
assert("typeof '7'.padStart is undefined", function () { return String(typeof "7".padStart); }, "undefined");
assertThrows("'7'.padStart(3, '0') throws", function () { return "7".padStart(3, "0"); });

/* 2. - 4. The documented workaround. */
function padStart(str, targetLen, padChar) {
    str = String(str);
    padChar = padChar || " ";
    while (str.length < targetLen) { str = padChar + str; }
    return str;
}
assert("workaround padStart('7', 3, '0') is '007'", function () { return String(padStart("7", 3, "0")); }, "007");
assert("workaround padStart('1234', 3, '0') is unchanged", function () { return String(padStart("1234", 3, "0")); }, "1234");
assert("workaround padStart('7', 3) pads with spaces", function () { return String(padStart("7", 3)); }, "  7");
</script>

padEnd

(ES6) — ❌ Missing. Append pad characters in a loop.

function padEnd(str, targetLen, padChar) {
    str = String(str);
    padChar = padChar || " ";
    while (str.length < targetLen) { str = str + padChar; }
    return str;
}
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: padEnd
 *
 * Proves:
 *   1. String.prototype.padEnd is MISSING: reading it yields undefined and
 *      calling it throws.
 *   2. The documented loop workaround pads on the RIGHT.
 *   3. The workaround leaves an already-long-enough string untouched.
 *   4. Its padChar defaults to a space.
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === 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");
}

/* 1. The member does not exist. */
assert("typeof '7'.padEnd is undefined", function () { return String(typeof "7".padEnd); }, "undefined");
assertThrows("'7'.padEnd(3, '0') throws", function () { return "7".padEnd(3, "0"); });

/* 2. - 4. The documented workaround. */
function padEnd(str, targetLen, padChar) {
    str = String(str);
    padChar = padChar || " ";
    while (str.length < targetLen) { str = str + padChar; }
    return str;
}
assert("workaround padEnd('7', 3, '0') is '700'", function () { return String(padEnd("7", 3, "0")); }, "700");
assert("workaround padEnd('1234', 3, '0') is unchanged", function () { return String(padEnd("1234", 3, "0")); }, "1234");
assert("workaround padEnd('7', 3) pads with spaces", function () { return String(padEnd("7", 3)); }, "7  ");
</script>

repeat

(ES6) — ❌ Missing. Concatenate in a loop.

function repeat(str, n) {
    var result = "";
    for (var i = 0; i < n; i++) { result += str; }
    return result;
}
repeat("ab", 3);   // "ababab"
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: repeat
 *
 * Proves:
 *   1. String.prototype.repeat is MISSING: reading it yields undefined and
 *      calling it throws.
 *   2. The documented loop workaround produces the repeated string:
 *      repeat("ab", 3) -> "ababab".
 *   3. A count of 0 yields the empty string.
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === 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");
}

/* 1. The member does not exist. */
assert("typeof 'ab'.repeat is undefined", function () { return String(typeof "ab".repeat); }, "undefined");
assertThrows("'ab'.repeat(3) throws", function () { return "ab".repeat(3); });

/* 2. + 3. The documented workaround. */
function repeat(str, n) {
    var result = "";
    for (var i = 0; i < n; i++) { result += str; }
    return result;
}
assert("workaround repeat('ab', 3) is 'ababab'", function () { return String(repeat("ab", 3)); }, "ababab");
assert("workaround repeat('ab', 0) is ''", function () { return String(repeat("ab", 0)); }, "");
</script>

codePointAt

(ES6) — ❌ Missing. Use charCodeAt for Basic Multilingual Plane characters.

"A".charCodeAt(0);   // 65  (codePointAt(0) for BMP chars)
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: codePointAt
 *
 * Proves:
 *   1. String.prototype.codePointAt is MISSING: reading it yields undefined
 *      and calling it throws.
 *   2. The documented replacement charCodeAt() returns 65 for "A", the
 *      value codePointAt(0) would return for a Basic Multilingual Plane
 *      character.
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, fn, expected) {
    var got;
    try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
    Platform.Response.Write((got === 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");
}

/* 1. The member does not exist. */
assert("typeof 'A'.codePointAt is undefined", function () { return String(typeof "A".codePointAt); }, "undefined");
assertThrows("'A'.codePointAt(0) throws", function () { return "A".codePointAt(0); });

/* 2. The documented replacement. */
assert("'A'.charCodeAt(0) is 65", function () { return String("A".charCodeAt(0)); }, "65");
</script>

See Also