Primitive Types

String

Strings use single or double quotes. No template literals (backticks).

var single = 'Hello';
var double = "World";
var combined = "Hello, " + "World!"; // concatenation only
var withNewline = "Line 1\nLine 2";
var withQuote = "He said \"hello\"";
var withTab = "col1\tcol2";

String length:

var str = "Hello";
var len = str.length; // 5

Number

Numbers are IEEE 754 doubles (same as JavaScript).

var integer = 42;
var decimal = 3.14;
var negative = -100;
var big = 1e6;   // 1000000
var nan = NaN;   // not a number
var inf = Infinity;

// Type checks
typeof 42 === "number"; // true
isNaN(NaN);             // true
isFinite(Infinity);     // false

Boolean

var yes = true;
var no  = false;

// Falsy values in SSJS (same as JS):
// false, 0, "", null, undefined, NaN

// Truthy: everything else, including "0", "false", [], {}
if ("false") { Write("Truthy!"); } // will execute

null

Explicitly no value. Use for intentionally empty variables.

var result = null;
typeof null === "object"; // true (JS quirk)
result === null;          // true

undefined

A variable that has been declared but not assigned.

var x;
typeof x === "undefined"; // true
x === undefined;          // true

SFMC quirk: Platform.Function.Lookup() returns a genuine null when no matching row is found, but a CLR null when the row exists and the field is empty — and that value throws on any coercion, so !result is unsafe. Wrap the result in String() and test for "null" or "". See Lookup.

Show test script
<script runat="server">
/*
 * Chapter: Primitive Types
 * Proves:
 *   1. string/number/boolean/null/undefined basics.
 *   2. 'false' string is truthy.
 * EXPECTED OUTPUT: every line starts with PASS.
 */
function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\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("concat", "Hello, " + "World!", "Hello, World!");
assert("len", "Hello".length, 5);
assert("typeof42", typeof 42, "number");
assert("isNaN", isNaN(NaN) ? "true" : "false", "true");
assert("isFinite Inf", isFinite(Infinity) ? "true" : "false", "false");
assert("typeof null", typeof null, "object");
var result = null; assert("===null", result === null ? "true" : "false", "true");
var x; assert("undef", typeof x, "undefined");
var branch = ""; if ("false") { branch = "Truthy!"; }
assert("false-string truthy", branch, "Truthy!");
</script>

Objects

Plain objects are key-value maps. Use them for structured data.

var subscriber = {
    key:   "abc123",
    email: "jane@example.com",
    active: true
};

// Access
var email = subscriber.email;        // dot notation
var key   = subscriber["key"];       // bracket notation

// Assign
subscriber.name = "Jane Smith";
subscriber["status"] = "confirmed";

// Check property exists
if (subscriber.hasOwnProperty("email")) {
    // safe to access
}

// Iterate — always use hasOwnProperty
for (var prop in subscriber) {
    if (subscriber.hasOwnProperty(prop)) {
        Write(prop + ": " + subscriber[prop] + "<br>");
    }
}

Always use hasOwnProperty in for...in loops. SFMC objects may have inherited enumerable properties (like _type) that you don’t want to process.

Show test script
<script runat="server">
/*
 * Chapter: Objects
 * Proves:
 *   1. dot/bracket/hasOwnProperty.
 * EXPECTED OUTPUT: every line starts with PASS.
 */
function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\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 subscriber = { key: "abc123", email: "jane@example.com", active: true };
assert("dot", subscriber.email, "jane@example.com");
assert("bracket", subscriber["key"], "abc123");
subscriber.name = "Jane Smith";
assert("assign", subscriber.name, "Jane Smith");
assert("hasOwn", subscriber.hasOwnProperty("email") ? "true" : "false", "true");
</script>

Arrays

Arrays are ordered lists. SSJS has ES3/5-safe array operations.

var fruits = ["apple", "banana", "cherry"];
var nums   = [1, 2, 3, 4, 5];
var mixed  = ["text", 42, true, null];

// Access and length
var first = fruits[0];   // "apple"
var len   = fruits.length; // 3

// Safe array methods (available in SSJS)
fruits.push("date");          // add to end (returns new length)
fruits.pop();                  // remove from end
fruits.shift();                // remove from front
fruits.unshift("avocado");     // add to front
fruits.reverse();              // in-place reverse
fruits.sort();                 // in-place sort (lexicographic)

// Slice (safe)
var sub = fruits.slice(1, 3);  // ["banana", "cherry"] — no mutation

// String join
var str = fruits.join(", ");   // "apple, banana, cherry"

Missing array methods — use polyfills or manual loops:

// ❌ These are NOT available in SSJS:
// arr.forEach(), arr.map(), arr.filter(), arr.find(), arr.indexOf()
// arr.every(), arr.some(), arr.reduce(), arr.includes()

// ✅ Manual equivalents:
for (var i = 0; i < arr.length; i++) {
    var item = arr[i];
    // process item
}

See Polyfills for ready-to-use Array method implementations.

Show test script
<script runat="server">
/*
 * Chapter: Arrays
 * Proves:
 *   1. push/pop/slice/join basics.
 * EXPECTED OUTPUT: every line starts with PASS.
 */
function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\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 fruits = ["apple", "banana", "cherry"];
assert("first", fruits[0], "apple");
assert("len", fruits.length, 3);
fruits.push("date");
assert("push len", fruits.length, 4);
fruits.pop();
var sub = ["apple", "banana", "cherry"].slice(1, 3);
assert("slice0", sub[0], "banana");
assert("join", ["a", "b"].join(", "), "a, b");
</script>

Type Conversions

// String to number
var n1 = Number("42");       // 42
var n2 = parseInt("42", 10); // 42 — argument must already be a clean integer string
var n3 = parseFloat("3.14"); // ~3.14 (IEEE float; do not compare with === to a decimal literal)
var n4 = "5" * 1;            // 5 (implicit)

// Number to string
var s1 = String(42);      // "42"
var s2 = 42 + "";         // "42" (concatenation coercion)
var s3 = (42).toString(); // "42"

// Boolean coercion
var b1 = Boolean(0);      // false
var b2 = Boolean("");     // false
var b3 = Boolean("0");    // true (non-empty string!)
var b4 = !!value;         // double-negation shorthand
Show test script
<script runat="server">
/*
 * Chapter: Type Conversions
 * Proves:
 *   1. Number/parseInt/String/Boolean on clean values.
 *   2. DEV: parseInt("42px") is NaN (browser JS: 42).
 *   3. parseFloat is IEEE — compare via Math.floor(x*100).
 * EXPECTED OUTPUT: every line starts with PASS.
 */
function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\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("Number", Number("42"), 42);
assert("parseInt clean", parseInt("42", 10), 42);
assert("DEV parseInt('42px') is NaN (browser: 42)", isNaN(parseInt("42px", 10)) ? "true" : "false", "true");
assert("parseFloat ~3.14", Math.floor(parseFloat("3.14") * 100), 314);
assert("implicit", "5" * 1, 5);
assert("String", String(42), "42");
assert("concat coerce", 42 + "", "42");
assert("toString", (42).toString(), "42");
assert("Boolean0", Boolean(0) ? "true" : "false", "false");
assert("Boolean empty", Boolean("") ? "true" : "false", "false");
assert("Boolean '0'", Boolean("0") ? "true" : "false", "true");
</script>

JSON

SSJS does not have JSON.parse or JSON.stringify. Use the SFMC alternatives:

// Parse JSON string → object
var obj = Platform.Function.ParseJSON(jsonString + "");
// The + "" keeps a non-string value a valid argument — objects/arrays throw

// Serialize object → JSON string
var jsonStr = Platform.Function.Stringify(obj);
Show test script
<script runat="server">
/*
 * Chapter: JSON
 * Proves:
 *   1. ParseJSON/Stringify round-trip.
 * EXPECTED OUTPUT: every line starts with PASS.
 */
function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\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 obj = { name: "Jane", n: 1 };
var jsonStr = Platform.Function.Stringify(obj);
var parsed = Platform.Function.ParseJSON(jsonStr + "");
assert("name", parsed.name, "Jane");
assert("n", parsed.n, 1);
</script>