[{"data":1,"prerenderedAt":3747},["ShallowReactive",2],{"page-\u002Fjs\u002F06-strings-and-template-literals":3},{"id":4,"title":5,"body":6,"description":3740,"extension":3741,"meta":3742,"navigation":58,"path":3743,"seo":3744,"stem":3745,"__hash__":3746},"content\u002Fjs\u002F06-strings-and-template-literals.md","JavaScript 06 — String Internals: UTF-16 Encoding, Surrogate Pairs & Tagged Templates",{"type":7,"value":8,"toc":3729},"minimark",[9,14,19,448,452,776,780,1187,1191,1774,1778,2396,2400,2810,2814,3222,3226,3546,3550,3554,3650,3725],[10,11,13],"h1",{"id":12},"_06-string-internals-utf-16-encoding-surrogate-pairs-tagged-templates","06 — String Internals: UTF-16 Encoding, Surrogate Pairs & Tagged Templates",[15,16,18],"h2",{"id":17},"utf-16-encoding-code-units-vs-code-points","UTF-16 Encoding: Code Units vs Code Points",[20,21,23],"code-wrapper",{"language":22},"javascript",[24,25,29],"pre",{"className":26,"code":27,"language":22,"meta":28,"style":28},"language-javascript shiki shiki-themes github-light github-dark","\u002F\u002F ── JS strings are UTF-16: each \"char\" is a 16-bit code unit ──\n\u002F\u002F Most ASCII\u002FLatin characters: 1 code unit (16 bits)\n\u002F\u002F Characters beyond U+FFFF (emoji, CJK, rare scripts): 2 code units (surrogate pair)\n\n\u002F\u002F ── .length counts CODE UNITS, not characters (visual chars) ──\n\"a\".length;       \u002F\u002F 1 (1 code unit)\n\"\\n\".length;      \u002F\u002F 1\n\"é\".length;       \u002F\u002F 2 (é = U+00E9, but can also be e + combining accent → 2 units)\n\"𝕏\".length;       \u002F\u002F 2! (𝕏 = U+1D54F, requires a surrogate pair: 2 code units)\n\"😀\".length;      \u002F\u002F 2! (emoji = U+1F600, surrogate pair: 2 code units)\n\n\u002F\u002F ── Surrogate pairs: characters outside the BMP (Basic Multilingual Plane, U+0000–U+FFFF) ──\n\u002F\u002F Encoded as two 16-bit code units: high surrogate (U+D800–U+DBFF) + low surrogate (U+DC00–U+DFFF)\nconst emoji = \"😀\";  \u002F\u002F U+1F600\nconsole.log(emoji.charCodeAt(0));  \u002F\u002F 55357 (0xD83D — high surrogate)\nconsole.log(emoji.charCodeAt(1));  \u002F\u002F 56800 (0xDE00 — low surrogate)\nconsole.log(emoji.codePointAt(0));  \u002F\u002F 128512 (0x1F600 — the actual code point!)\n\n\u002F\u002F ── .charAt() and index access break on surrogate pairs ──\nconsole.log(emoji.charAt(0));  \u002F\u002F '\\uD83D' (high surrogate only — invalid\u002Fgarbage)\nconsole.log(emoji[0]);         \u002F\u002F '\\uD83D' (same — index gives a code unit, not a character)\n\n\u002F\u002F ── .codePointAt() and String.fromCodePoint() handle full code points ──\nconsole.log(\"😀\".codePointAt(0));  \u002F\u002F 128512 (the actual code point)\nconsole.log(String.fromCodePoint(128512));  \u002F\u002F \"😀\" (from code point to string)\nconsole.log(String.fromCharCode(128512));  \u002F\u002F \"\\uD800\\uDE00\"? No — fromCharCode only does code units\n\u002F\u002F String.fromCharCode(0xD83D, 0xDE00) → \"😀\" (manual surrogate pair construction)\n\n\u002F\u002F ── Iterating with for...of iterates by CODE POINT (not code unit) ──\nfor (const ch of \"😀abc\") { console.log(ch); }\n\u002F\u002F \"😀\" (1 iteration — full code point), \"a\", \"b\", \"c\" (4 total iterations)\n\u002F\u002F But: \"😀abc\".length is 5 (2 code units for emoji + 3 for abc)\n","",[30,31,32,41,47,53,60,66,87,108,123,138,153,158,164,170,192,220,241,262,267,273,294,312,317,323,347,370,391,397,402,408,436,442],"code",{"__ignoreMap":28},[33,34,37],"span",{"class":35,"line":36},"line",1,[33,38,40],{"class":39},"sdCPZ","\u002F\u002F ── JS strings are UTF-16: each \"char\" is a 16-bit code unit ──\n",[33,42,44],{"class":35,"line":43},2,[33,45,46],{"class":39},"\u002F\u002F Most ASCII\u002FLatin characters: 1 code unit (16 bits)\n",[33,48,50],{"class":35,"line":49},3,[33,51,52],{"class":39},"\u002F\u002F Characters beyond U+FFFF (emoji, CJK, rare scripts): 2 code units (surrogate pair)\n",[33,54,56],{"class":35,"line":55},4,[33,57,59],{"emptyLinePlaceholder":58},true,"\n",[33,61,63],{"class":35,"line":62},5,[33,64,65],{"class":39},"\u002F\u002F ── .length counts CODE UNITS, not characters (visual chars) ──\n",[33,67,69,73,77,81,84],{"class":35,"line":68},6,[33,70,72],{"class":71},"sJ6F3","\"a\"",[33,74,76],{"class":75},"ssxIu",".",[33,78,80],{"class":79},"snvgF","length",[33,82,83],{"class":75},";       ",[33,85,86],{"class":39},"\u002F\u002F 1 (1 code unit)\n",[33,88,90,93,96,98,100,102,105],{"class":35,"line":89},7,[33,91,92],{"class":71},"\"",[33,94,95],{"class":79},"\\n",[33,97,92],{"class":71},[33,99,76],{"class":75},[33,101,80],{"class":79},[33,103,104],{"class":75},";      ",[33,106,107],{"class":39},"\u002F\u002F 1\n",[33,109,111,114,116,118,120],{"class":35,"line":110},8,[33,112,113],{"class":71},"\"é\"",[33,115,76],{"class":75},[33,117,80],{"class":79},[33,119,83],{"class":75},[33,121,122],{"class":39},"\u002F\u002F 2 (é = U+00E9, but can also be e + combining accent → 2 units)\n",[33,124,126,129,131,133,135],{"class":35,"line":125},9,[33,127,128],{"class":71},"\"𝕏\"",[33,130,76],{"class":75},[33,132,80],{"class":79},[33,134,83],{"class":75},[33,136,137],{"class":39},"\u002F\u002F 2! (𝕏 = U+1D54F, requires a surrogate pair: 2 code units)\n",[33,139,141,144,146,148,150],{"class":35,"line":140},10,[33,142,143],{"class":71},"\"😀\"",[33,145,76],{"class":75},[33,147,80],{"class":79},[33,149,104],{"class":75},[33,151,152],{"class":39},"\u002F\u002F 2! (emoji = U+1F600, surrogate pair: 2 code units)\n",[33,154,156],{"class":35,"line":155},11,[33,157,59],{"emptyLinePlaceholder":58},[33,159,161],{"class":35,"line":160},12,[33,162,163],{"class":39},"\u002F\u002F ── Surrogate pairs: characters outside the BMP (Basic Multilingual Plane, U+0000–U+FFFF) ──\n",[33,165,167],{"class":35,"line":166},13,[33,168,169],{"class":39},"\u002F\u002F Encoded as two 16-bit code units: high surrogate (U+D800–U+DBFF) + low surrogate (U+DC00–U+DFFF)\n",[33,171,173,177,180,183,186,189],{"class":35,"line":172},14,[33,174,176],{"class":175},"svdQ7","const",[33,178,179],{"class":79}," emoji",[33,181,182],{"class":175}," =",[33,184,185],{"class":71}," \"😀\"",[33,187,188],{"class":75},";  ",[33,190,191],{"class":39},"\u002F\u002F U+1F600\n",[33,193,195,198,202,205,208,211,214,217],{"class":35,"line":194},15,[33,196,197],{"class":75},"console.",[33,199,201],{"class":200},"sIsaT","log",[33,203,204],{"class":75},"(emoji.",[33,206,207],{"class":200},"charCodeAt",[33,209,210],{"class":75},"(",[33,212,213],{"class":79},"0",[33,215,216],{"class":75},"));  ",[33,218,219],{"class":39},"\u002F\u002F 55357 (0xD83D — high surrogate)\n",[33,221,223,225,227,229,231,233,236,238],{"class":35,"line":222},16,[33,224,197],{"class":75},[33,226,201],{"class":200},[33,228,204],{"class":75},[33,230,207],{"class":200},[33,232,210],{"class":75},[33,234,235],{"class":79},"1",[33,237,216],{"class":75},[33,239,240],{"class":39},"\u002F\u002F 56800 (0xDE00 — low surrogate)\n",[33,242,244,246,248,250,253,255,257,259],{"class":35,"line":243},17,[33,245,197],{"class":75},[33,247,201],{"class":200},[33,249,204],{"class":75},[33,251,252],{"class":200},"codePointAt",[33,254,210],{"class":75},[33,256,213],{"class":79},[33,258,216],{"class":75},[33,260,261],{"class":39},"\u002F\u002F 128512 (0x1F600 — the actual code point!)\n",[33,263,265],{"class":35,"line":264},18,[33,266,59],{"emptyLinePlaceholder":58},[33,268,270],{"class":35,"line":269},19,[33,271,272],{"class":39},"\u002F\u002F ── .charAt() and index access break on surrogate pairs ──\n",[33,274,276,278,280,282,285,287,289,291],{"class":35,"line":275},20,[33,277,197],{"class":75},[33,279,201],{"class":200},[33,281,204],{"class":75},[33,283,284],{"class":200},"charAt",[33,286,210],{"class":75},[33,288,213],{"class":79},[33,290,216],{"class":75},[33,292,293],{"class":39},"\u002F\u002F '\\uD83D' (high surrogate only — invalid\u002Fgarbage)\n",[33,295,297,299,301,304,306,309],{"class":35,"line":296},21,[33,298,197],{"class":75},[33,300,201],{"class":200},[33,302,303],{"class":75},"(emoji[",[33,305,213],{"class":79},[33,307,308],{"class":75},"]);         ",[33,310,311],{"class":39},"\u002F\u002F '\\uD83D' (same — index gives a code unit, not a character)\n",[33,313,315],{"class":35,"line":314},22,[33,316,59],{"emptyLinePlaceholder":58},[33,318,320],{"class":35,"line":319},23,[33,321,322],{"class":39},"\u002F\u002F ── .codePointAt() and String.fromCodePoint() handle full code points ──\n",[33,324,326,328,330,332,334,336,338,340,342,344],{"class":35,"line":325},24,[33,327,197],{"class":75},[33,329,201],{"class":200},[33,331,210],{"class":75},[33,333,143],{"class":71},[33,335,76],{"class":75},[33,337,252],{"class":200},[33,339,210],{"class":75},[33,341,213],{"class":79},[33,343,216],{"class":75},[33,345,346],{"class":39},"\u002F\u002F 128512 (the actual code point)\n",[33,348,350,352,354,357,360,362,365,367],{"class":35,"line":349},25,[33,351,197],{"class":75},[33,353,201],{"class":200},[33,355,356],{"class":75},"(String.",[33,358,359],{"class":200},"fromCodePoint",[33,361,210],{"class":75},[33,363,364],{"class":79},"128512",[33,366,216],{"class":75},[33,368,369],{"class":39},"\u002F\u002F \"😀\" (from code point to string)\n",[33,371,373,375,377,379,382,384,386,388],{"class":35,"line":372},26,[33,374,197],{"class":75},[33,376,201],{"class":200},[33,378,356],{"class":75},[33,380,381],{"class":200},"fromCharCode",[33,383,210],{"class":75},[33,385,364],{"class":79},[33,387,216],{"class":75},[33,389,390],{"class":39},"\u002F\u002F \"\\uD800\\uDE00\"? No — fromCharCode only does code units\n",[33,392,394],{"class":35,"line":393},27,[33,395,396],{"class":39},"\u002F\u002F String.fromCharCode(0xD83D, 0xDE00) → \"😀\" (manual surrogate pair construction)\n",[33,398,400],{"class":35,"line":399},28,[33,401,59],{"emptyLinePlaceholder":58},[33,403,405],{"class":35,"line":404},29,[33,406,407],{"class":39},"\u002F\u002F ── Iterating with for...of iterates by CODE POINT (not code unit) ──\n",[33,409,411,414,417,419,422,425,428,431,433],{"class":35,"line":410},30,[33,412,413],{"class":175},"for",[33,415,416],{"class":75}," (",[33,418,176],{"class":175},[33,420,421],{"class":79}," ch",[33,423,424],{"class":175}," of",[33,426,427],{"class":71}," \"😀abc\"",[33,429,430],{"class":75},") { console.",[33,432,201],{"class":200},[33,434,435],{"class":75},"(ch); }\n",[33,437,439],{"class":35,"line":438},31,[33,440,441],{"class":39},"\u002F\u002F \"😀\" (1 iteration — full code point), \"a\", \"b\", \"c\" (4 total iterations)\n",[33,443,445],{"class":35,"line":444},32,[33,446,447],{"class":39},"\u002F\u002F But: \"😀abc\".length is 5 (2 code units for emoji + 3 for abc)\n",[15,449,451],{"id":450},"anti-pattern-string-reversal-with-surrogate-pairs","Anti-Pattern: String Reversal with Surrogate Pairs",[20,453,454],{"language":22},[24,455,457],{"className":26,"code":456,"language":22,"meta":28,"style":28},"\u002F\u002F ❌ NAIVE — .split(\"\").reverse().join(\"\") breaks on surrogate pairs and combining marks\nconst str = \"Hello 🌍\";  \u002F\u002F 🌍 = U+1F30D (surrogate pair)\nconst broken = str.split(\"\").reverse().join(\"\");\nconsole.log(broken);  \u002F\u002F \"�� olleH\" — the emoji is corrupted (surrogates reversed individually)\n\u002F\u002F split(\"\") splits by code unit — the surrogate pair is split into two separate units\n\u002F\u002F and reversed independently, creating an invalid character sequence.\n\n\u002F\u002F ✅ CORRECT — use the string iterator (which iterates by code point)\nconst reversed = [...str].reverse().join(\"\");\nconsole.log(reversed);  \u002F\u002F \"🌍 olleH\" — emoji preserved (spread iterates by code point)\n\n\u002F\u002F ✅ ALSO CORRECT — Array.from (also iterates by code point)\nconst reversed2 = Array.from(str).reverse().join(\"\");\n\n\u002F\u002F ── Combining marks are still a problem (e.g., é = e + ̀) ──\nconst accented = \"café\";  \u002F\u002F é might be U+00E9 (1 unit) or e + U+0301 (2 units)\n\u002F\u002F Reversing \"café\" (decomposed: c a e ́) → \"éfac\" (accent moved to the wrong letter)\n\u002F\u002F For full Unicode-aware reversal, use Intl.Segmenter (ES2022):\nconst segmenter = new Intl.Segmenter(\"en\", { granularity: \"grapheme\" });\nconst graphemes = [...segmenter.segment(\"café🇫🇷\")].map(s => s.segment);\nconst reversed3 = graphemes.reverse().join(\"\");\n\u002F\u002F Intl.Segmenter splits into grapheme clusters (visual characters), handling\n\u002F\u002F combining marks, emoji sequences, and flag emoji (regional indicator pairs).\n",[30,458,459,464,481,520,532,537,542,546,551,581,593,597,602,632,636,641,658,663,668,700,742,766,771],{"__ignoreMap":28},[33,460,461],{"class":35,"line":36},[33,462,463],{"class":39},"\u002F\u002F ❌ NAIVE — .split(\"\").reverse().join(\"\") breaks on surrogate pairs and combining marks\n",[33,465,466,468,471,473,476,478],{"class":35,"line":43},[33,467,176],{"class":175},[33,469,470],{"class":79}," str",[33,472,182],{"class":175},[33,474,475],{"class":71}," \"Hello 🌍\"",[33,477,188],{"class":75},[33,479,480],{"class":39},"\u002F\u002F 🌍 = U+1F30D (surrogate pair)\n",[33,482,483,485,488,490,493,496,498,501,504,507,510,513,515,517],{"class":35,"line":49},[33,484,176],{"class":175},[33,486,487],{"class":79}," broken",[33,489,182],{"class":175},[33,491,492],{"class":75}," str.",[33,494,495],{"class":200},"split",[33,497,210],{"class":75},[33,499,500],{"class":71},"\"\"",[33,502,503],{"class":75},").",[33,505,506],{"class":200},"reverse",[33,508,509],{"class":75},"().",[33,511,512],{"class":200},"join",[33,514,210],{"class":75},[33,516,500],{"class":71},[33,518,519],{"class":75},");\n",[33,521,522,524,526,529],{"class":35,"line":55},[33,523,197],{"class":75},[33,525,201],{"class":200},[33,527,528],{"class":75},"(broken);  ",[33,530,531],{"class":39},"\u002F\u002F \"�� olleH\" — the emoji is corrupted (surrogates reversed individually)\n",[33,533,534],{"class":35,"line":62},[33,535,536],{"class":39},"\u002F\u002F split(\"\") splits by code unit — the surrogate pair is split into two separate units\n",[33,538,539],{"class":35,"line":68},[33,540,541],{"class":39},"\u002F\u002F and reversed independently, creating an invalid character sequence.\n",[33,543,544],{"class":35,"line":89},[33,545,59],{"emptyLinePlaceholder":58},[33,547,548],{"class":35,"line":110},[33,549,550],{"class":39},"\u002F\u002F ✅ CORRECT — use the string iterator (which iterates by code point)\n",[33,552,553,555,558,560,563,566,569,571,573,575,577,579],{"class":35,"line":125},[33,554,176],{"class":175},[33,556,557],{"class":79}," reversed",[33,559,182],{"class":175},[33,561,562],{"class":75}," [",[33,564,565],{"class":175},"...",[33,567,568],{"class":75},"str].",[33,570,506],{"class":200},[33,572,509],{"class":75},[33,574,512],{"class":200},[33,576,210],{"class":75},[33,578,500],{"class":71},[33,580,519],{"class":75},[33,582,583,585,587,590],{"class":35,"line":140},[33,584,197],{"class":75},[33,586,201],{"class":200},[33,588,589],{"class":75},"(reversed);  ",[33,591,592],{"class":39},"\u002F\u002F \"🌍 olleH\" — emoji preserved (spread iterates by code point)\n",[33,594,595],{"class":35,"line":155},[33,596,59],{"emptyLinePlaceholder":58},[33,598,599],{"class":35,"line":160},[33,600,601],{"class":39},"\u002F\u002F ✅ ALSO CORRECT — Array.from (also iterates by code point)\n",[33,603,604,606,609,611,614,617,620,622,624,626,628,630],{"class":35,"line":166},[33,605,176],{"class":175},[33,607,608],{"class":79}," reversed2",[33,610,182],{"class":175},[33,612,613],{"class":75}," Array.",[33,615,616],{"class":200},"from",[33,618,619],{"class":75},"(str).",[33,621,506],{"class":200},[33,623,509],{"class":75},[33,625,512],{"class":200},[33,627,210],{"class":75},[33,629,500],{"class":71},[33,631,519],{"class":75},[33,633,634],{"class":35,"line":172},[33,635,59],{"emptyLinePlaceholder":58},[33,637,638],{"class":35,"line":194},[33,639,640],{"class":39},"\u002F\u002F ── Combining marks are still a problem (e.g., é = e + ̀) ──\n",[33,642,643,645,648,650,653,655],{"class":35,"line":222},[33,644,176],{"class":175},[33,646,647],{"class":79}," accented",[33,649,182],{"class":175},[33,651,652],{"class":71}," \"café\"",[33,654,188],{"class":75},[33,656,657],{"class":39},"\u002F\u002F é might be U+00E9 (1 unit) or e + U+0301 (2 units)\n",[33,659,660],{"class":35,"line":243},[33,661,662],{"class":39},"\u002F\u002F Reversing \"café\" (decomposed: c a e ́) → \"éfac\" (accent moved to the wrong letter)\n",[33,664,665],{"class":35,"line":264},[33,666,667],{"class":39},"\u002F\u002F For full Unicode-aware reversal, use Intl.Segmenter (ES2022):\n",[33,669,670,672,675,677,680,683,686,688,691,694,697],{"class":35,"line":269},[33,671,176],{"class":175},[33,673,674],{"class":79}," segmenter",[33,676,182],{"class":175},[33,678,679],{"class":175}," new",[33,681,682],{"class":75}," Intl.",[33,684,685],{"class":200},"Segmenter",[33,687,210],{"class":75},[33,689,690],{"class":71},"\"en\"",[33,692,693],{"class":75},", { granularity: ",[33,695,696],{"class":71},"\"grapheme\"",[33,698,699],{"class":75}," });\n",[33,701,702,704,707,709,711,713,716,719,721,724,727,730,732,736,739],{"class":35,"line":275},[33,703,176],{"class":175},[33,705,706],{"class":79}," graphemes",[33,708,182],{"class":175},[33,710,562],{"class":75},[33,712,565],{"class":175},[33,714,715],{"class":75},"segmenter.",[33,717,718],{"class":200},"segment",[33,720,210],{"class":75},[33,722,723],{"class":71},"\"café🇫🇷\"",[33,725,726],{"class":75},")].",[33,728,729],{"class":200},"map",[33,731,210],{"class":75},[33,733,735],{"class":734},"sCrzJ","s",[33,737,738],{"class":175}," =>",[33,740,741],{"class":75}," s.segment);\n",[33,743,744,746,749,751,754,756,758,760,762,764],{"class":35,"line":296},[33,745,176],{"class":175},[33,747,748],{"class":79}," reversed3",[33,750,182],{"class":175},[33,752,753],{"class":75}," graphemes.",[33,755,506],{"class":200},[33,757,509],{"class":75},[33,759,512],{"class":200},[33,761,210],{"class":75},[33,763,500],{"class":71},[33,765,519],{"class":75},[33,767,768],{"class":35,"line":314},[33,769,770],{"class":39},"\u002F\u002F Intl.Segmenter splits into grapheme clusters (visual characters), handling\n",[33,772,773],{"class":35,"line":319},[33,774,775],{"class":39},"\u002F\u002F combining marks, emoji sequences, and flag emoji (regional indicator pairs).\n",[15,777,779],{"id":778},"template-literals-and-tagged-templates","Template Literals and Tagged Templates",[20,781,782],{"language":22},[24,783,785],{"className":26,"code":784,"language":22,"meta":28,"style":28},"\u002F\u002F ── Template literal: string interpolation with backticks ──\nconst name = \"Alice\";\nconst age = 30;\nconsole.log(`Hello, ${name}! You are ${age} years old.`);\n\u002F\u002F Expressions inside ${}:\nconsole.log(`2 + 2 = ${2 + 2}`);   \u002F\u002F \"2 + 2 = 4\"\nconsole.log(`Upper: ${name.toUpperCase()}`);  \u002F\u002F \"Upper: ALICE\"\nconsole.log(`Nested: ${`inner ${name}`}`);     \u002F\u002F \"Nested: inner Alice\"\n\n\u002F\u002F ── Multiline strings (no \\n needed) ──\nconst html = `\n\u003Cdiv>\n    \u003Ch1>${name}\u003C\u002Fh1>\n    \u003Cp>Age: ${age}\u003C\u002Fp>\n\u003C\u002Fdiv>\n`;\n\u002F\u002F The newline after the opening backtick and before the closing backtick are part of the string.\n\n\u002F\u002F ── Tagged templates: process template literal with a function ──\n\u002F\u002F The tag function receives: (strings[], ...values)\n\u002F\u002F strings: array of literal string parts (between ${} interpolations)\n\u002F\u002F values: array of interpolated expressions\nfunction tag(strings, ...values) {\n    console.log(strings);  \u002F\u002F [\"Hello, \", \"! You are \", \" years old.\", raw: [...]]\n    console.log(values);   \u002F\u002F [\"Alice\", 30]\n    return strings.reduce((result, str, i) =>\n        result + str + (values[i] !== undefined ? `[${values[i]}]` : \"\"), \"\");\n}\nconst tagged = tag`Hello, ${name}! You are ${age} years old.`;\n\u002F\u002F \"Hello, [Alice]! You are [30] years old.\"\n",[30,786,787,792,807,821,846,851,880,909,931,935,940,952,957,967,977,982,989,994,998,1003,1008,1013,1018,1042,1055,1067,1100,1154,1159,1182],{"__ignoreMap":28},[33,788,789],{"class":35,"line":36},[33,790,791],{"class":39},"\u002F\u002F ── Template literal: string interpolation with backticks ──\n",[33,793,794,796,799,801,804],{"class":35,"line":43},[33,795,176],{"class":175},[33,797,798],{"class":79}," name",[33,800,182],{"class":175},[33,802,803],{"class":71}," \"Alice\"",[33,805,806],{"class":75},";\n",[33,808,809,811,814,816,819],{"class":35,"line":49},[33,810,176],{"class":175},[33,812,813],{"class":79}," age",[33,815,182],{"class":175},[33,817,818],{"class":79}," 30",[33,820,806],{"class":75},[33,822,823,825,827,829,832,835,838,841,844],{"class":35,"line":55},[33,824,197],{"class":75},[33,826,201],{"class":200},[33,828,210],{"class":75},[33,830,831],{"class":71},"`Hello, ${",[33,833,834],{"class":75},"name",[33,836,837],{"class":71},"}! You are ${",[33,839,840],{"class":75},"age",[33,842,843],{"class":71},"} years old.`",[33,845,519],{"class":75},[33,847,848],{"class":35,"line":62},[33,849,850],{"class":39},"\u002F\u002F Expressions inside ${}:\n",[33,852,853,855,857,859,862,865,868,871,874,877],{"class":35,"line":68},[33,854,197],{"class":75},[33,856,201],{"class":200},[33,858,210],{"class":75},[33,860,861],{"class":71},"`2 + 2 = ${",[33,863,864],{"class":79},"2",[33,866,867],{"class":175}," +",[33,869,870],{"class":79}," 2",[33,872,873],{"class":71},"}`",[33,875,876],{"class":75},");   ",[33,878,879],{"class":39},"\u002F\u002F \"2 + 2 = 4\"\n",[33,881,882,884,886,888,891,893,895,898,901,903,906],{"class":35,"line":89},[33,883,197],{"class":75},[33,885,201],{"class":200},[33,887,210],{"class":75},[33,889,890],{"class":71},"`Upper: ${",[33,892,834],{"class":75},[33,894,76],{"class":71},[33,896,897],{"class":200},"toUpperCase",[33,899,900],{"class":71},"()",[33,902,873],{"class":71},[33,904,905],{"class":75},");  ",[33,907,908],{"class":39},"\u002F\u002F \"Upper: ALICE\"\n",[33,910,911,913,915,917,920,922,925,928],{"class":35,"line":110},[33,912,197],{"class":75},[33,914,201],{"class":200},[33,916,210],{"class":75},[33,918,919],{"class":71},"`Nested: ${`inner ${",[33,921,834],{"class":75},[33,923,924],{"class":71},"}`}`",[33,926,927],{"class":75},");     ",[33,929,930],{"class":39},"\u002F\u002F \"Nested: inner Alice\"\n",[33,932,933],{"class":35,"line":125},[33,934,59],{"emptyLinePlaceholder":58},[33,936,937],{"class":35,"line":140},[33,938,939],{"class":39},"\u002F\u002F ── Multiline strings (no \\n needed) ──\n",[33,941,942,944,947,949],{"class":35,"line":155},[33,943,176],{"class":175},[33,945,946],{"class":79}," html",[33,948,182],{"class":175},[33,950,951],{"class":71}," `\n",[33,953,954],{"class":35,"line":160},[33,955,956],{"class":71},"\u003Cdiv>\n",[33,958,959,962,964],{"class":35,"line":166},[33,960,961],{"class":71},"    \u003Ch1>${",[33,963,834],{"class":75},[33,965,966],{"class":71},"}\u003C\u002Fh1>\n",[33,968,969,972,974],{"class":35,"line":172},[33,970,971],{"class":71},"    \u003Cp>Age: ${",[33,973,840],{"class":75},[33,975,976],{"class":71},"}\u003C\u002Fp>\n",[33,978,979],{"class":35,"line":194},[33,980,981],{"class":71},"\u003C\u002Fdiv>\n",[33,983,984,987],{"class":35,"line":222},[33,985,986],{"class":71},"`",[33,988,806],{"class":75},[33,990,991],{"class":35,"line":243},[33,992,993],{"class":39},"\u002F\u002F The newline after the opening backtick and before the closing backtick are part of the string.\n",[33,995,996],{"class":35,"line":264},[33,997,59],{"emptyLinePlaceholder":58},[33,999,1000],{"class":35,"line":269},[33,1001,1002],{"class":39},"\u002F\u002F ── Tagged templates: process template literal with a function ──\n",[33,1004,1005],{"class":35,"line":275},[33,1006,1007],{"class":39},"\u002F\u002F The tag function receives: (strings[], ...values)\n",[33,1009,1010],{"class":35,"line":296},[33,1011,1012],{"class":39},"\u002F\u002F strings: array of literal string parts (between ${} interpolations)\n",[33,1014,1015],{"class":35,"line":314},[33,1016,1017],{"class":39},"\u002F\u002F values: array of interpolated expressions\n",[33,1019,1020,1023,1026,1028,1031,1034,1036,1039],{"class":35,"line":319},[33,1021,1022],{"class":175},"function",[33,1024,1025],{"class":200}," tag",[33,1027,210],{"class":75},[33,1029,1030],{"class":734},"strings",[33,1032,1033],{"class":75},", ",[33,1035,565],{"class":175},[33,1037,1038],{"class":734},"values",[33,1040,1041],{"class":75},") {\n",[33,1043,1044,1047,1049,1052],{"class":35,"line":325},[33,1045,1046],{"class":75},"    console.",[33,1048,201],{"class":200},[33,1050,1051],{"class":75},"(strings);  ",[33,1053,1054],{"class":39},"\u002F\u002F [\"Hello, \", \"! You are \", \" years old.\", raw: [...]]\n",[33,1056,1057,1059,1061,1064],{"class":35,"line":349},[33,1058,1046],{"class":75},[33,1060,201],{"class":200},[33,1062,1063],{"class":75},"(values);   ",[33,1065,1066],{"class":39},"\u002F\u002F [\"Alice\", 30]\n",[33,1068,1069,1072,1075,1078,1081,1084,1086,1089,1091,1094,1097],{"class":35,"line":372},[33,1070,1071],{"class":175},"    return",[33,1073,1074],{"class":75}," strings.",[33,1076,1077],{"class":200},"reduce",[33,1079,1080],{"class":75},"((",[33,1082,1083],{"class":734},"result",[33,1085,1033],{"class":75},[33,1087,1088],{"class":734},"str",[33,1090,1033],{"class":75},[33,1092,1093],{"class":734},"i",[33,1095,1096],{"class":75},") ",[33,1098,1099],{"class":175},"=>\n",[33,1101,1102,1105,1108,1111,1113,1116,1119,1122,1125,1128,1130,1133,1135,1138,1141,1144,1147,1150,1152],{"class":35,"line":393},[33,1103,1104],{"class":75},"        result ",[33,1106,1107],{"class":175},"+",[33,1109,1110],{"class":75}," str ",[33,1112,1107],{"class":175},[33,1114,1115],{"class":75}," (values[i] ",[33,1117,1118],{"class":175},"!==",[33,1120,1121],{"class":79}," undefined",[33,1123,1124],{"class":175}," ?",[33,1126,1127],{"class":71}," `[${",[33,1129,1038],{"class":75},[33,1131,1132],{"class":71},"[",[33,1134,1093],{"class":75},[33,1136,1137],{"class":71},"]",[33,1139,1140],{"class":71},"}]`",[33,1142,1143],{"class":175}," :",[33,1145,1146],{"class":71}," \"\"",[33,1148,1149],{"class":75},"), ",[33,1151,500],{"class":71},[33,1153,519],{"class":75},[33,1155,1156],{"class":35,"line":399},[33,1157,1158],{"class":75},"}\n",[33,1160,1161,1163,1166,1168,1170,1172,1174,1176,1178,1180],{"class":35,"line":404},[33,1162,176],{"class":175},[33,1164,1165],{"class":79}," tagged",[33,1167,182],{"class":175},[33,1169,1025],{"class":200},[33,1171,831],{"class":71},[33,1173,834],{"class":75},[33,1175,837],{"class":71},[33,1177,840],{"class":75},[33,1179,843],{"class":71},[33,1181,806],{"class":75},[33,1183,1184],{"class":35,"line":410},[33,1185,1186],{"class":39},"\u002F\u002F \"Hello, [Alice]! You are [30] years old.\"\n",[15,1188,1190],{"id":1189},"production-pattern-html-escaping-with-tagged-templates","Production Pattern: HTML Escaping with Tagged Templates",[20,1192,1193],{"language":22},[24,1194,1196],{"className":26,"code":1195,"language":22,"meta":28,"style":28},"\u002F\u002F ── Safe HTML template tag (prevents XSS) ──\nfunction html(strings, ...values) {\n    \u002F\u002F strings.raw contains the raw (unescaped) template parts\n    \u002F\u002F values contains the interpolated expressions\n    return strings.reduce((result, str, i) => {\n        const value = values[i];\n        if (value === undefined) return result + str;\n\n        \u002F\u002F Escape HTML special characters in interpolated values\n        const escaped = String(value)\n            .replace(\u002F&\u002Fg, \"&amp;\")\n            .replace(\u002F\u003C\u002Fg, \"&lt;\")\n            .replace(\u002F>\u002Fg, \"&gt;\")\n            .replace(\u002F\"\u002Fg, \"&quot;\")\n            .replace(\u002F'\u002Fg, \"&#39;\");\n\n        return result + str + escaped;\n    }, \"\");\n}\n\nconst userInput = '\u003Cscript>alert(\"xss\")\u003C\u002Fscript>';\nconst safe = html`\u003Cdiv>${userInput}\u003C\u002Fdiv>`;\n\u002F\u002F \u003Cdiv>&lt;script&gt;alert(&quot;xss&quot;)&lt;\u002Fscript&gt;\u003C\u002Fdiv>\n\u002F\u002F The script tag is escaped — displayed as text, not executed.\n\n\u002F\u002F ── Conditional values and arrays ──\nfunction html2(strings, ...values) {\n    return strings.reduce((result, str, i) => {\n        const value = values[i];\n        if (value === undefined || value === null || value === false) return result + str;\n        if (Array.isArray(value)) return result + str + value.join(\"\");\n        return result + str + String(value);\n    }, \"\");\n}\nconst items = [\"apple\", \"banana\"];\nconst list = html2`\u003Cul>${items.map(i => html2`\u003Cli>${i}\u003C\u002Fli>`)}\u003C\u002Ful>`;\n\u002F\u002F \u003Cul>\u003Cli>apple\u003C\u002Fli>\u003Cli>banana\u003C\u002Fli>\u003C\u002Ful>\n",[30,1197,1198,1203,1221,1226,1231,1259,1272,1298,1302,1307,1322,1352,1376,1400,1423,1447,1451,1467,1476,1480,1484,1498,1520,1525,1530,1534,1539,1558,1584,1594,1634,1668,1685,1694,1699,1722,1768],{"__ignoreMap":28},[33,1199,1200],{"class":35,"line":36},[33,1201,1202],{"class":39},"\u002F\u002F ── Safe HTML template tag (prevents XSS) ──\n",[33,1204,1205,1207,1209,1211,1213,1215,1217,1219],{"class":35,"line":43},[33,1206,1022],{"class":175},[33,1208,946],{"class":200},[33,1210,210],{"class":75},[33,1212,1030],{"class":734},[33,1214,1033],{"class":75},[33,1216,565],{"class":175},[33,1218,1038],{"class":734},[33,1220,1041],{"class":75},[33,1222,1223],{"class":35,"line":49},[33,1224,1225],{"class":39},"    \u002F\u002F strings.raw contains the raw (unescaped) template parts\n",[33,1227,1228],{"class":35,"line":55},[33,1229,1230],{"class":39},"    \u002F\u002F values contains the interpolated expressions\n",[33,1232,1233,1235,1237,1239,1241,1243,1245,1247,1249,1251,1253,1256],{"class":35,"line":62},[33,1234,1071],{"class":175},[33,1236,1074],{"class":75},[33,1238,1077],{"class":200},[33,1240,1080],{"class":75},[33,1242,1083],{"class":734},[33,1244,1033],{"class":75},[33,1246,1088],{"class":734},[33,1248,1033],{"class":75},[33,1250,1093],{"class":734},[33,1252,1096],{"class":75},[33,1254,1255],{"class":175},"=>",[33,1257,1258],{"class":75}," {\n",[33,1260,1261,1264,1267,1269],{"class":35,"line":68},[33,1262,1263],{"class":175},"        const",[33,1265,1266],{"class":79}," value",[33,1268,182],{"class":175},[33,1270,1271],{"class":75}," values[i];\n",[33,1273,1274,1277,1280,1283,1285,1287,1290,1293,1295],{"class":35,"line":89},[33,1275,1276],{"class":175},"        if",[33,1278,1279],{"class":75}," (value ",[33,1281,1282],{"class":175},"===",[33,1284,1121],{"class":79},[33,1286,1096],{"class":75},[33,1288,1289],{"class":175},"return",[33,1291,1292],{"class":75}," result ",[33,1294,1107],{"class":175},[33,1296,1297],{"class":75}," str;\n",[33,1299,1300],{"class":35,"line":110},[33,1301,59],{"emptyLinePlaceholder":58},[33,1303,1304],{"class":35,"line":125},[33,1305,1306],{"class":39},"        \u002F\u002F Escape HTML special characters in interpolated values\n",[33,1308,1309,1311,1314,1316,1319],{"class":35,"line":140},[33,1310,1263],{"class":175},[33,1312,1313],{"class":79}," escaped",[33,1315,182],{"class":175},[33,1317,1318],{"class":200}," String",[33,1320,1321],{"class":75},"(value)\n",[33,1323,1324,1327,1330,1332,1335,1339,1341,1344,1346,1349],{"class":35,"line":155},[33,1325,1326],{"class":75},"            .",[33,1328,1329],{"class":200},"replace",[33,1331,210],{"class":75},[33,1333,1334],{"class":71},"\u002F",[33,1336,1338],{"class":1337},"svAP2","&",[33,1340,1334],{"class":71},[33,1342,1343],{"class":175},"g",[33,1345,1033],{"class":75},[33,1347,1348],{"class":71},"\"&amp;\"",[33,1350,1351],{"class":75},")\n",[33,1353,1354,1356,1358,1360,1362,1365,1367,1369,1371,1374],{"class":35,"line":160},[33,1355,1326],{"class":75},[33,1357,1329],{"class":200},[33,1359,210],{"class":75},[33,1361,1334],{"class":71},[33,1363,1364],{"class":1337},"\u003C",[33,1366,1334],{"class":71},[33,1368,1343],{"class":175},[33,1370,1033],{"class":75},[33,1372,1373],{"class":71},"\"&lt;\"",[33,1375,1351],{"class":75},[33,1377,1378,1380,1382,1384,1386,1389,1391,1393,1395,1398],{"class":35,"line":166},[33,1379,1326],{"class":75},[33,1381,1329],{"class":200},[33,1383,210],{"class":75},[33,1385,1334],{"class":71},[33,1387,1388],{"class":1337},">",[33,1390,1334],{"class":71},[33,1392,1343],{"class":175},[33,1394,1033],{"class":75},[33,1396,1397],{"class":71},"\"&gt;\"",[33,1399,1351],{"class":75},[33,1401,1402,1404,1406,1408,1410,1412,1414,1416,1418,1421],{"class":35,"line":172},[33,1403,1326],{"class":75},[33,1405,1329],{"class":200},[33,1407,210],{"class":75},[33,1409,1334],{"class":71},[33,1411,92],{"class":1337},[33,1413,1334],{"class":71},[33,1415,1343],{"class":175},[33,1417,1033],{"class":75},[33,1419,1420],{"class":71},"\"&quot;\"",[33,1422,1351],{"class":75},[33,1424,1425,1427,1429,1431,1433,1436,1438,1440,1442,1445],{"class":35,"line":194},[33,1426,1326],{"class":75},[33,1428,1329],{"class":200},[33,1430,210],{"class":75},[33,1432,1334],{"class":71},[33,1434,1435],{"class":1337},"'",[33,1437,1334],{"class":71},[33,1439,1343],{"class":175},[33,1441,1033],{"class":75},[33,1443,1444],{"class":71},"\"&#39;\"",[33,1446,519],{"class":75},[33,1448,1449],{"class":35,"line":222},[33,1450,59],{"emptyLinePlaceholder":58},[33,1452,1453,1456,1458,1460,1462,1464],{"class":35,"line":243},[33,1454,1455],{"class":175},"        return",[33,1457,1292],{"class":75},[33,1459,1107],{"class":175},[33,1461,1110],{"class":75},[33,1463,1107],{"class":175},[33,1465,1466],{"class":75}," escaped;\n",[33,1468,1469,1472,1474],{"class":35,"line":264},[33,1470,1471],{"class":75},"    }, ",[33,1473,500],{"class":71},[33,1475,519],{"class":75},[33,1477,1478],{"class":35,"line":269},[33,1479,1158],{"class":75},[33,1481,1482],{"class":35,"line":275},[33,1483,59],{"emptyLinePlaceholder":58},[33,1485,1486,1488,1491,1493,1496],{"class":35,"line":296},[33,1487,176],{"class":175},[33,1489,1490],{"class":79}," userInput",[33,1492,182],{"class":175},[33,1494,1495],{"class":71}," '\u003Cscript>alert(\"xss\")\u003C\u002Fscript>'",[33,1497,806],{"class":75},[33,1499,1500,1502,1505,1507,1509,1512,1515,1518],{"class":35,"line":314},[33,1501,176],{"class":175},[33,1503,1504],{"class":79}," safe",[33,1506,182],{"class":175},[33,1508,946],{"class":200},[33,1510,1511],{"class":71},"`\u003Cdiv>${",[33,1513,1514],{"class":75},"userInput",[33,1516,1517],{"class":71},"}\u003C\u002Fdiv>`",[33,1519,806],{"class":75},[33,1521,1522],{"class":35,"line":319},[33,1523,1524],{"class":39},"\u002F\u002F \u003Cdiv>&lt;script&gt;alert(&quot;xss&quot;)&lt;\u002Fscript&gt;\u003C\u002Fdiv>\n",[33,1526,1527],{"class":35,"line":325},[33,1528,1529],{"class":39},"\u002F\u002F The script tag is escaped — displayed as text, not executed.\n",[33,1531,1532],{"class":35,"line":349},[33,1533,59],{"emptyLinePlaceholder":58},[33,1535,1536],{"class":35,"line":372},[33,1537,1538],{"class":39},"\u002F\u002F ── Conditional values and arrays ──\n",[33,1540,1541,1543,1546,1548,1550,1552,1554,1556],{"class":35,"line":393},[33,1542,1022],{"class":175},[33,1544,1545],{"class":200}," html2",[33,1547,210],{"class":75},[33,1549,1030],{"class":734},[33,1551,1033],{"class":75},[33,1553,565],{"class":175},[33,1555,1038],{"class":734},[33,1557,1041],{"class":75},[33,1559,1560,1562,1564,1566,1568,1570,1572,1574,1576,1578,1580,1582],{"class":35,"line":399},[33,1561,1071],{"class":175},[33,1563,1074],{"class":75},[33,1565,1077],{"class":200},[33,1567,1080],{"class":75},[33,1569,1083],{"class":734},[33,1571,1033],{"class":75},[33,1573,1088],{"class":734},[33,1575,1033],{"class":75},[33,1577,1093],{"class":734},[33,1579,1096],{"class":75},[33,1581,1255],{"class":175},[33,1583,1258],{"class":75},[33,1585,1586,1588,1590,1592],{"class":35,"line":404},[33,1587,1263],{"class":175},[33,1589,1266],{"class":79},[33,1591,182],{"class":175},[33,1593,1271],{"class":75},[33,1595,1596,1598,1600,1602,1604,1607,1610,1612,1615,1617,1619,1621,1624,1626,1628,1630,1632],{"class":35,"line":410},[33,1597,1276],{"class":175},[33,1599,1279],{"class":75},[33,1601,1282],{"class":175},[33,1603,1121],{"class":79},[33,1605,1606],{"class":175}," ||",[33,1608,1609],{"class":75}," value ",[33,1611,1282],{"class":175},[33,1613,1614],{"class":79}," null",[33,1616,1606],{"class":175},[33,1618,1609],{"class":75},[33,1620,1282],{"class":175},[33,1622,1623],{"class":79}," false",[33,1625,1096],{"class":75},[33,1627,1289],{"class":175},[33,1629,1292],{"class":75},[33,1631,1107],{"class":175},[33,1633,1297],{"class":75},[33,1635,1636,1638,1641,1644,1647,1649,1651,1653,1655,1657,1660,1662,1664,1666],{"class":35,"line":438},[33,1637,1276],{"class":175},[33,1639,1640],{"class":75}," (Array.",[33,1642,1643],{"class":200},"isArray",[33,1645,1646],{"class":75},"(value)) ",[33,1648,1289],{"class":175},[33,1650,1292],{"class":75},[33,1652,1107],{"class":175},[33,1654,1110],{"class":75},[33,1656,1107],{"class":175},[33,1658,1659],{"class":75}," value.",[33,1661,512],{"class":200},[33,1663,210],{"class":75},[33,1665,500],{"class":71},[33,1667,519],{"class":75},[33,1669,1670,1672,1674,1676,1678,1680,1682],{"class":35,"line":444},[33,1671,1455],{"class":175},[33,1673,1292],{"class":75},[33,1675,1107],{"class":175},[33,1677,1110],{"class":75},[33,1679,1107],{"class":175},[33,1681,1318],{"class":200},[33,1683,1684],{"class":75},"(value);\n",[33,1686,1688,1690,1692],{"class":35,"line":1687},33,[33,1689,1471],{"class":75},[33,1691,500],{"class":71},[33,1693,519],{"class":75},[33,1695,1697],{"class":35,"line":1696},34,[33,1698,1158],{"class":75},[33,1700,1702,1704,1707,1709,1711,1714,1716,1719],{"class":35,"line":1701},35,[33,1703,176],{"class":175},[33,1705,1706],{"class":79}," items",[33,1708,182],{"class":175},[33,1710,562],{"class":75},[33,1712,1713],{"class":71},"\"apple\"",[33,1715,1033],{"class":75},[33,1717,1718],{"class":71},"\"banana\"",[33,1720,1721],{"class":75},"];\n",[33,1723,1725,1727,1730,1732,1734,1737,1740,1742,1744,1746,1748,1750,1752,1755,1757,1760,1763,1766],{"class":35,"line":1724},36,[33,1726,176],{"class":175},[33,1728,1729],{"class":79}," list",[33,1731,182],{"class":175},[33,1733,1545],{"class":200},[33,1735,1736],{"class":71},"`\u003Cul>${",[33,1738,1739],{"class":75},"items",[33,1741,76],{"class":71},[33,1743,729],{"class":200},[33,1745,210],{"class":71},[33,1747,1093],{"class":79},[33,1749,738],{"class":175},[33,1751,1545],{"class":200},[33,1753,1754],{"class":71},"`\u003Cli>${",[33,1756,1093],{"class":75},[33,1758,1759],{"class":71},"}\u003C\u002Fli>`",[33,1761,1762],{"class":71},")",[33,1764,1765],{"class":71},"}\u003C\u002Ful>`",[33,1767,806],{"class":75},[33,1769,1771],{"class":35,"line":1770},37,[33,1772,1773],{"class":39},"\u002F\u002F \u003Cul>\u003Cli>apple\u003C\u002Fli>\u003Cli>banana\u003C\u002Fli>\u003C\u002Ful>\n",[15,1775,1777],{"id":1776},"string-methods-finding-and-extracting","String Methods: Finding and Extracting",[20,1779,1780],{"language":22},[24,1781,1783],{"className":26,"code":1782,"language":22,"meta":28,"style":28},"\u002F\u002F ── Finding ──\n\"hello world\".indexOf(\"world\");  \u002F\u002F 6 (index, or -1 if not found)\n\"hello world\".includes(\"world\"); \u002F\u002F true (ES2015 — boolean, no index needed)\n\"hello\".startsWith(\"he\");        \u002F\u002F true\n\"hello\".endsWith(\"lo\");          \u002F\u002F true\n\"hello world\".search(\u002Fworld\u002F);  \u002F\u002F 6 (regex — returns index, not match)\n\"hello world\".match(\u002Fo\u002Fg);      \u002F\u002F [\"o\", \"o\"] (regex match — array of matches)\n\"hello\".matchAll(\u002Fl\u002Fg);          \u002F\u002F iterator of match objects (ES2020)\n[...\"hello\".matchAll(\u002Fl\u002Fg)];    \u002F\u002F [Match, Match] (each with index, groups, etc.)\n\n\u002F\u002F ── Extracting ──\n\"hello world\".slice(0, 5);       \u002F\u002F \"hello\" (start, end — negative allowed)\n\"hello world\".slice(6);         \u002F\u002F \"world\" (start to end)\n\"hello world\".slice(-5);        \u002F\u002F \"world\" (negative = from end)\n\"hello world\".substring(0, 5);  \u002F\u002F \"hello\" (like slice, but NO negative args)\n\"hello world\".substr(0, 5);     \u002F\u002F \"hello\" (start, length — DEPRECATED, avoid)\n\"hello\".at(0);                  \u002F\u002F \"h\" (ES2022 — supports negative: .at(-1) = \"o\")\n\"hello\".at(-1);                 \u002F\u002F \"o\" (negative index — from end)\n\n\u002F\u002F ── ⚠️ .substring() vs .slice() — negative behavior differs ──\n\"hello\".slice(-3);       \u002F\u002F \"llo\" (negative = from end)\n\"hello\".substring(-3);   \u002F\u002F \"hello\" (negative treated as 0)\n\"hello\".substring(4, 1); \u002F\u002F \"ell\" (SWAPS args if start > end!)\n\"hello\".slice(4, 1);     \u002F\u002F \"\" (no swap — empty if start > end)\n\u002F\u002F Always use .slice() — it has consistent negative-index behavior.\n\n\u002F\u002F ── .replace() vs .replaceAll() ──\n\"aaa\".replace(\"a\", \"b\");    \u002F\u002F \"baa\" (replaces FIRST match only)\n\"aaa\".replace(\u002Fa\u002Fg, \"b\");   \u002F\u002F \"bbb\" (regex \u002Fg replaces ALL)\n\"aaa\".replaceAll(\"a\", \"b\"); \u002F\u002F \"bbb\" (ES2021 — replaces ALL, no regex needed)\n\u002F\u002F ⚠️ .replace with a string pattern only replaces the FIRST occurrence.\n\n\u002F\u002F ── ⚠️ .replace() with regex \u002Fg has STATE (lastIndex persists on the regex object) ──\nconst re = \u002Fa\u002Fg;\n\"aaa\".replace(re, \"b\");  \u002F\u002F \"bbb\"\n\"aaa\".replace(re, \"b\");  \u002F\u002F \"bbb\" — wait, this might be wrong with stateful regex\n\u002F\u002F Actually: .replace() with \u002Fg is fine — it resets internally. But .test() and .exec() persist:\nre.lastIndex = 0;  \u002F\u002F must reset lastIndex for stateful regex with .test()\u002F.exec()\n",[30,1784,1785,1790,1810,1829,1850,1869,1892,1918,1943,1971,1975,1980,2004,2023,2043,2065,2087,2106,2126,2130,2135,2155,2174,2196,2217,2222,2226,2231,2255,2283,2305,2310,2314,2319,2339,2357,2374,2379],{"__ignoreMap":28},[33,1786,1787],{"class":35,"line":36},[33,1788,1789],{"class":39},"\u002F\u002F ── Finding ──\n",[33,1791,1792,1795,1797,1800,1802,1805,1807],{"class":35,"line":43},[33,1793,1794],{"class":71},"\"hello world\"",[33,1796,76],{"class":75},[33,1798,1799],{"class":200},"indexOf",[33,1801,210],{"class":75},[33,1803,1804],{"class":71},"\"world\"",[33,1806,905],{"class":75},[33,1808,1809],{"class":39},"\u002F\u002F 6 (index, or -1 if not found)\n",[33,1811,1812,1814,1816,1819,1821,1823,1826],{"class":35,"line":49},[33,1813,1794],{"class":71},[33,1815,76],{"class":75},[33,1817,1818],{"class":200},"includes",[33,1820,210],{"class":75},[33,1822,1804],{"class":71},[33,1824,1825],{"class":75},"); ",[33,1827,1828],{"class":39},"\u002F\u002F true (ES2015 — boolean, no index needed)\n",[33,1830,1831,1834,1836,1839,1841,1844,1847],{"class":35,"line":55},[33,1832,1833],{"class":71},"\"hello\"",[33,1835,76],{"class":75},[33,1837,1838],{"class":200},"startsWith",[33,1840,210],{"class":75},[33,1842,1843],{"class":71},"\"he\"",[33,1845,1846],{"class":75},");        ",[33,1848,1849],{"class":39},"\u002F\u002F true\n",[33,1851,1852,1854,1856,1859,1861,1864,1867],{"class":35,"line":62},[33,1853,1833],{"class":71},[33,1855,76],{"class":75},[33,1857,1858],{"class":200},"endsWith",[33,1860,210],{"class":75},[33,1862,1863],{"class":71},"\"lo\"",[33,1865,1866],{"class":75},");          ",[33,1868,1849],{"class":39},[33,1870,1871,1873,1875,1878,1880,1882,1885,1887,1889],{"class":35,"line":68},[33,1872,1794],{"class":71},[33,1874,76],{"class":75},[33,1876,1877],{"class":200},"search",[33,1879,210],{"class":75},[33,1881,1334],{"class":71},[33,1883,1884],{"class":1337},"world",[33,1886,1334],{"class":71},[33,1888,905],{"class":75},[33,1890,1891],{"class":39},"\u002F\u002F 6 (regex — returns index, not match)\n",[33,1893,1894,1896,1898,1901,1903,1905,1908,1910,1912,1915],{"class":35,"line":89},[33,1895,1794],{"class":71},[33,1897,76],{"class":75},[33,1899,1900],{"class":200},"match",[33,1902,210],{"class":75},[33,1904,1334],{"class":71},[33,1906,1907],{"class":1337},"o",[33,1909,1334],{"class":71},[33,1911,1343],{"class":175},[33,1913,1914],{"class":75},");      ",[33,1916,1917],{"class":39},"\u002F\u002F [\"o\", \"o\"] (regex match — array of matches)\n",[33,1919,1920,1922,1924,1927,1929,1931,1934,1936,1938,1940],{"class":35,"line":110},[33,1921,1833],{"class":71},[33,1923,76],{"class":75},[33,1925,1926],{"class":200},"matchAll",[33,1928,210],{"class":75},[33,1930,1334],{"class":71},[33,1932,1933],{"class":1337},"l",[33,1935,1334],{"class":71},[33,1937,1343],{"class":175},[33,1939,1866],{"class":75},[33,1941,1942],{"class":39},"\u002F\u002F iterator of match objects (ES2020)\n",[33,1944,1945,1947,1949,1951,1953,1955,1957,1959,1961,1963,1965,1968],{"class":35,"line":125},[33,1946,1132],{"class":75},[33,1948,565],{"class":175},[33,1950,1833],{"class":71},[33,1952,76],{"class":75},[33,1954,1926],{"class":200},[33,1956,210],{"class":75},[33,1958,1334],{"class":71},[33,1960,1933],{"class":1337},[33,1962,1334],{"class":71},[33,1964,1343],{"class":175},[33,1966,1967],{"class":75},")];    ",[33,1969,1970],{"class":39},"\u002F\u002F [Match, Match] (each with index, groups, etc.)\n",[33,1972,1973],{"class":35,"line":140},[33,1974,59],{"emptyLinePlaceholder":58},[33,1976,1977],{"class":35,"line":155},[33,1978,1979],{"class":39},"\u002F\u002F ── Extracting ──\n",[33,1981,1982,1984,1986,1989,1991,1993,1995,1998,2001],{"class":35,"line":160},[33,1983,1794],{"class":71},[33,1985,76],{"class":75},[33,1987,1988],{"class":200},"slice",[33,1990,210],{"class":75},[33,1992,213],{"class":79},[33,1994,1033],{"class":75},[33,1996,1997],{"class":79},"5",[33,1999,2000],{"class":75},");       ",[33,2002,2003],{"class":39},"\u002F\u002F \"hello\" (start, end — negative allowed)\n",[33,2005,2006,2008,2010,2012,2014,2017,2020],{"class":35,"line":166},[33,2007,1794],{"class":71},[33,2009,76],{"class":75},[33,2011,1988],{"class":200},[33,2013,210],{"class":75},[33,2015,2016],{"class":79},"6",[33,2018,2019],{"class":75},");         ",[33,2021,2022],{"class":39},"\u002F\u002F \"world\" (start to end)\n",[33,2024,2025,2027,2029,2031,2033,2036,2038,2040],{"class":35,"line":172},[33,2026,1794],{"class":71},[33,2028,76],{"class":75},[33,2030,1988],{"class":200},[33,2032,210],{"class":75},[33,2034,2035],{"class":175},"-",[33,2037,1997],{"class":79},[33,2039,1846],{"class":75},[33,2041,2042],{"class":39},"\u002F\u002F \"world\" (negative = from end)\n",[33,2044,2045,2047,2049,2052,2054,2056,2058,2060,2062],{"class":35,"line":194},[33,2046,1794],{"class":71},[33,2048,76],{"class":75},[33,2050,2051],{"class":200},"substring",[33,2053,210],{"class":75},[33,2055,213],{"class":79},[33,2057,1033],{"class":75},[33,2059,1997],{"class":79},[33,2061,905],{"class":75},[33,2063,2064],{"class":39},"\u002F\u002F \"hello\" (like slice, but NO negative args)\n",[33,2066,2067,2069,2071,2074,2076,2078,2080,2082,2084],{"class":35,"line":222},[33,2068,1794],{"class":71},[33,2070,76],{"class":75},[33,2072,2073],{"class":200},"substr",[33,2075,210],{"class":75},[33,2077,213],{"class":79},[33,2079,1033],{"class":75},[33,2081,1997],{"class":79},[33,2083,927],{"class":75},[33,2085,2086],{"class":39},"\u002F\u002F \"hello\" (start, length — DEPRECATED, avoid)\n",[33,2088,2089,2091,2093,2096,2098,2100,2103],{"class":35,"line":243},[33,2090,1833],{"class":71},[33,2092,76],{"class":75},[33,2094,2095],{"class":200},"at",[33,2097,210],{"class":75},[33,2099,213],{"class":79},[33,2101,2102],{"class":75},");                  ",[33,2104,2105],{"class":39},"\u002F\u002F \"h\" (ES2022 — supports negative: .at(-1) = \"o\")\n",[33,2107,2108,2110,2112,2114,2116,2118,2120,2123],{"class":35,"line":264},[33,2109,1833],{"class":71},[33,2111,76],{"class":75},[33,2113,2095],{"class":200},[33,2115,210],{"class":75},[33,2117,2035],{"class":175},[33,2119,235],{"class":79},[33,2121,2122],{"class":75},");                 ",[33,2124,2125],{"class":39},"\u002F\u002F \"o\" (negative index — from end)\n",[33,2127,2128],{"class":35,"line":269},[33,2129,59],{"emptyLinePlaceholder":58},[33,2131,2132],{"class":35,"line":275},[33,2133,2134],{"class":39},"\u002F\u002F ── ⚠️ .substring() vs .slice() — negative behavior differs ──\n",[33,2136,2137,2139,2141,2143,2145,2147,2150,2152],{"class":35,"line":296},[33,2138,1833],{"class":71},[33,2140,76],{"class":75},[33,2142,1988],{"class":200},[33,2144,210],{"class":75},[33,2146,2035],{"class":175},[33,2148,2149],{"class":79},"3",[33,2151,2000],{"class":75},[33,2153,2154],{"class":39},"\u002F\u002F \"llo\" (negative = from end)\n",[33,2156,2157,2159,2161,2163,2165,2167,2169,2171],{"class":35,"line":314},[33,2158,1833],{"class":71},[33,2160,76],{"class":75},[33,2162,2051],{"class":200},[33,2164,210],{"class":75},[33,2166,2035],{"class":175},[33,2168,2149],{"class":79},[33,2170,876],{"class":75},[33,2172,2173],{"class":39},"\u002F\u002F \"hello\" (negative treated as 0)\n",[33,2175,2176,2178,2180,2182,2184,2187,2189,2191,2193],{"class":35,"line":319},[33,2177,1833],{"class":71},[33,2179,76],{"class":75},[33,2181,2051],{"class":200},[33,2183,210],{"class":75},[33,2185,2186],{"class":79},"4",[33,2188,1033],{"class":75},[33,2190,235],{"class":79},[33,2192,1825],{"class":75},[33,2194,2195],{"class":39},"\u002F\u002F \"ell\" (SWAPS args if start > end!)\n",[33,2197,2198,2200,2202,2204,2206,2208,2210,2212,2214],{"class":35,"line":325},[33,2199,1833],{"class":71},[33,2201,76],{"class":75},[33,2203,1988],{"class":200},[33,2205,210],{"class":75},[33,2207,2186],{"class":79},[33,2209,1033],{"class":75},[33,2211,235],{"class":79},[33,2213,927],{"class":75},[33,2215,2216],{"class":39},"\u002F\u002F \"\" (no swap — empty if start > end)\n",[33,2218,2219],{"class":35,"line":349},[33,2220,2221],{"class":39},"\u002F\u002F Always use .slice() — it has consistent negative-index behavior.\n",[33,2223,2224],{"class":35,"line":372},[33,2225,59],{"emptyLinePlaceholder":58},[33,2227,2228],{"class":35,"line":393},[33,2229,2230],{"class":39},"\u002F\u002F ── .replace() vs .replaceAll() ──\n",[33,2232,2233,2236,2238,2240,2242,2244,2246,2249,2252],{"class":35,"line":399},[33,2234,2235],{"class":71},"\"aaa\"",[33,2237,76],{"class":75},[33,2239,1329],{"class":200},[33,2241,210],{"class":75},[33,2243,72],{"class":71},[33,2245,1033],{"class":75},[33,2247,2248],{"class":71},"\"b\"",[33,2250,2251],{"class":75},");    ",[33,2253,2254],{"class":39},"\u002F\u002F \"baa\" (replaces FIRST match only)\n",[33,2256,2257,2259,2261,2263,2265,2267,2270,2272,2274,2276,2278,2280],{"class":35,"line":404},[33,2258,2235],{"class":71},[33,2260,76],{"class":75},[33,2262,1329],{"class":200},[33,2264,210],{"class":75},[33,2266,1334],{"class":71},[33,2268,2269],{"class":1337},"a",[33,2271,1334],{"class":71},[33,2273,1343],{"class":175},[33,2275,1033],{"class":75},[33,2277,2248],{"class":71},[33,2279,876],{"class":75},[33,2281,2282],{"class":39},"\u002F\u002F \"bbb\" (regex \u002Fg replaces ALL)\n",[33,2284,2285,2287,2289,2292,2294,2296,2298,2300,2302],{"class":35,"line":410},[33,2286,2235],{"class":71},[33,2288,76],{"class":75},[33,2290,2291],{"class":200},"replaceAll",[33,2293,210],{"class":75},[33,2295,72],{"class":71},[33,2297,1033],{"class":75},[33,2299,2248],{"class":71},[33,2301,1825],{"class":75},[33,2303,2304],{"class":39},"\u002F\u002F \"bbb\" (ES2021 — replaces ALL, no regex needed)\n",[33,2306,2307],{"class":35,"line":438},[33,2308,2309],{"class":39},"\u002F\u002F ⚠️ .replace with a string pattern only replaces the FIRST occurrence.\n",[33,2311,2312],{"class":35,"line":444},[33,2313,59],{"emptyLinePlaceholder":58},[33,2315,2316],{"class":35,"line":1687},[33,2317,2318],{"class":39},"\u002F\u002F ── ⚠️ .replace() with regex \u002Fg has STATE (lastIndex persists on the regex object) ──\n",[33,2320,2321,2323,2326,2328,2331,2333,2335,2337],{"class":35,"line":1696},[33,2322,176],{"class":175},[33,2324,2325],{"class":79}," re",[33,2327,182],{"class":175},[33,2329,2330],{"class":71}," \u002F",[33,2332,2269],{"class":1337},[33,2334,1334],{"class":71},[33,2336,1343],{"class":175},[33,2338,806],{"class":75},[33,2340,2341,2343,2345,2347,2350,2352,2354],{"class":35,"line":1701},[33,2342,2235],{"class":71},[33,2344,76],{"class":75},[33,2346,1329],{"class":200},[33,2348,2349],{"class":75},"(re, ",[33,2351,2248],{"class":71},[33,2353,905],{"class":75},[33,2355,2356],{"class":39},"\u002F\u002F \"bbb\"\n",[33,2358,2359,2361,2363,2365,2367,2369,2371],{"class":35,"line":1724},[33,2360,2235],{"class":71},[33,2362,76],{"class":75},[33,2364,1329],{"class":200},[33,2366,2349],{"class":75},[33,2368,2248],{"class":71},[33,2370,905],{"class":75},[33,2372,2373],{"class":39},"\u002F\u002F \"bbb\" — wait, this might be wrong with stateful regex\n",[33,2375,2376],{"class":35,"line":1770},[33,2377,2378],{"class":39},"\u002F\u002F Actually: .replace() with \u002Fg is fine — it resets internally. But .test() and .exec() persist:\n",[33,2380,2382,2385,2388,2391,2393],{"class":35,"line":2381},38,[33,2383,2384],{"class":75},"re.lastIndex ",[33,2386,2387],{"class":175},"=",[33,2389,2390],{"class":79}," 0",[33,2392,188],{"class":75},[33,2394,2395],{"class":39},"\u002F\u002F must reset lastIndex for stateful regex with .test()\u002F.exec()\n",[15,2397,2399],{"id":2398},"unicode-normalization","Unicode Normalization",[20,2401,2402],{"language":22},[24,2403,2405],{"className":26,"code":2404,"language":22,"meta":28,"style":28},"\u002F\u002F ── The same character can have multiple representations ──\n\u002F\u002F \"é\" can be:\n\u002F\u002F   1. Precomposed: U+00E9 (é) — 1 code unit\n\u002F\u002F   2. Decomposed: U+0065 (e) + U+0301 (combining acute accent) — 2 code units\n\nconst precomposed = \"é\";        \u002F\u002F U+00E9\nconst decomposed = \"e\\u0301\";   \u002F\u002F e + combining accent\nconsole.log(precomposed === decomposed);  \u002F\u002F false! (different code units)\nconsole.log(precomposed.length);           \u002F\u002F 1\nconsole.log(decomposed.length);            \u002F\u002F 2\n\n\u002F\u002F ── .normalize() converts to a canonical form ──\nconst norm1 = precomposed.normalize(\"NFC\");  \u002F\u002F composed form (U+00E9)\nconst norm2 = decomposed.normalize(\"NFC\");  \u002F\u002F composed form (U+00E9)\nconsole.log(norm1 === norm2);  \u002F\u002F true (both are the same composed form)\n\n\u002F\u002F ── Normalization forms ──\n\u002F\u002F NFC  (Canonical Composition): precomposed chars (default, best for storage\u002Fdisplay)\n\u002F\u002F NFD  (Canonical Decomposition): decomposed chars (base + combining marks)\n\u002F\u002F NFKC (Compatibility Composition): like NFC + compatibility decomposition (e.g., ﬁ → fi)\n\u002F\u002F NFKD (Compatibility Decomposition): like NFD + compatibility decomposition\n\n\u002F\u002F ── When normalization matters ──\nconst names = [\"café\", \"cafe\\u0301\"];  \u002F\u002F both \"café\" but different code units\nconst unique = new Set(names);  \u002F\u002F Set has 2 entries (they're not equal!)\nconst unique2 = new Set(names.map(n => n.normalize(\"NFC\")));  \u002F\u002F Set has 1 entry (normalized)\n\u002F\u002F Always normalize before comparison, hashing, or deduplication of Unicode strings.\n\n\u002F\u002F ── Collation with Intl.Collator (locale-aware sorting) ──\nconst words = [\"café\", \"cafe\", \" Café\", \"café\"];\nwords.sort();  \u002F\u002F default sort: code unit order (not locale-aware)\nwords.sort(new Intl.Collator(\"en\").compare);  \u002F\u002F locale-aware sort\n\u002F\u002F Intl.Collator handles accents, case, and locale-specific ordering rules.\n",[30,2406,2407,2412,2417,2422,2427,2431,2449,2472,2489,2505,2522,2526,2531,2556,2578,2595,2599,2604,2609,2614,2619,2624,2628,2633,2662,2682,2722,2727,2731,2736,2765,2779,2805],{"__ignoreMap":28},[33,2408,2409],{"class":35,"line":36},[33,2410,2411],{"class":39},"\u002F\u002F ── The same character can have multiple representations ──\n",[33,2413,2414],{"class":35,"line":43},[33,2415,2416],{"class":39},"\u002F\u002F \"é\" can be:\n",[33,2418,2419],{"class":35,"line":49},[33,2420,2421],{"class":39},"\u002F\u002F   1. Precomposed: U+00E9 (é) — 1 code unit\n",[33,2423,2424],{"class":35,"line":55},[33,2425,2426],{"class":39},"\u002F\u002F   2. Decomposed: U+0065 (e) + U+0301 (combining acute accent) — 2 code units\n",[33,2428,2429],{"class":35,"line":62},[33,2430,59],{"emptyLinePlaceholder":58},[33,2432,2433,2435,2438,2440,2443,2446],{"class":35,"line":68},[33,2434,176],{"class":175},[33,2436,2437],{"class":79}," precomposed",[33,2439,182],{"class":175},[33,2441,2442],{"class":71}," \"é\"",[33,2444,2445],{"class":75},";        ",[33,2447,2448],{"class":39},"\u002F\u002F U+00E9\n",[33,2450,2451,2453,2456,2458,2461,2464,2466,2469],{"class":35,"line":89},[33,2452,176],{"class":175},[33,2454,2455],{"class":79}," decomposed",[33,2457,182],{"class":175},[33,2459,2460],{"class":71}," \"e",[33,2462,2463],{"class":79},"\\u0301",[33,2465,92],{"class":71},[33,2467,2468],{"class":75},";   ",[33,2470,2471],{"class":39},"\u002F\u002F e + combining accent\n",[33,2473,2474,2476,2478,2481,2483,2486],{"class":35,"line":110},[33,2475,197],{"class":75},[33,2477,201],{"class":200},[33,2479,2480],{"class":75},"(precomposed ",[33,2482,1282],{"class":175},[33,2484,2485],{"class":75}," decomposed);  ",[33,2487,2488],{"class":39},"\u002F\u002F false! (different code units)\n",[33,2490,2491,2493,2495,2498,2500,2503],{"class":35,"line":125},[33,2492,197],{"class":75},[33,2494,201],{"class":200},[33,2496,2497],{"class":75},"(precomposed.",[33,2499,80],{"class":79},[33,2501,2502],{"class":75},");           ",[33,2504,107],{"class":39},[33,2506,2507,2509,2511,2514,2516,2519],{"class":35,"line":140},[33,2508,197],{"class":75},[33,2510,201],{"class":200},[33,2512,2513],{"class":75},"(decomposed.",[33,2515,80],{"class":79},[33,2517,2518],{"class":75},");            ",[33,2520,2521],{"class":39},"\u002F\u002F 2\n",[33,2523,2524],{"class":35,"line":155},[33,2525,59],{"emptyLinePlaceholder":58},[33,2527,2528],{"class":35,"line":160},[33,2529,2530],{"class":39},"\u002F\u002F ── .normalize() converts to a canonical form ──\n",[33,2532,2533,2535,2538,2540,2543,2546,2548,2551,2553],{"class":35,"line":166},[33,2534,176],{"class":175},[33,2536,2537],{"class":79}," norm1",[33,2539,182],{"class":175},[33,2541,2542],{"class":75}," precomposed.",[33,2544,2545],{"class":200},"normalize",[33,2547,210],{"class":75},[33,2549,2550],{"class":71},"\"NFC\"",[33,2552,905],{"class":75},[33,2554,2555],{"class":39},"\u002F\u002F composed form (U+00E9)\n",[33,2557,2558,2560,2563,2565,2568,2570,2572,2574,2576],{"class":35,"line":172},[33,2559,176],{"class":175},[33,2561,2562],{"class":79}," norm2",[33,2564,182],{"class":175},[33,2566,2567],{"class":75}," decomposed.",[33,2569,2545],{"class":200},[33,2571,210],{"class":75},[33,2573,2550],{"class":71},[33,2575,905],{"class":75},[33,2577,2555],{"class":39},[33,2579,2580,2582,2584,2587,2589,2592],{"class":35,"line":194},[33,2581,197],{"class":75},[33,2583,201],{"class":200},[33,2585,2586],{"class":75},"(norm1 ",[33,2588,1282],{"class":175},[33,2590,2591],{"class":75}," norm2);  ",[33,2593,2594],{"class":39},"\u002F\u002F true (both are the same composed form)\n",[33,2596,2597],{"class":35,"line":222},[33,2598,59],{"emptyLinePlaceholder":58},[33,2600,2601],{"class":35,"line":243},[33,2602,2603],{"class":39},"\u002F\u002F ── Normalization forms ──\n",[33,2605,2606],{"class":35,"line":264},[33,2607,2608],{"class":39},"\u002F\u002F NFC  (Canonical Composition): precomposed chars (default, best for storage\u002Fdisplay)\n",[33,2610,2611],{"class":35,"line":269},[33,2612,2613],{"class":39},"\u002F\u002F NFD  (Canonical Decomposition): decomposed chars (base + combining marks)\n",[33,2615,2616],{"class":35,"line":275},[33,2617,2618],{"class":39},"\u002F\u002F NFKC (Compatibility Composition): like NFC + compatibility decomposition (e.g., ﬁ → fi)\n",[33,2620,2621],{"class":35,"line":296},[33,2622,2623],{"class":39},"\u002F\u002F NFKD (Compatibility Decomposition): like NFD + compatibility decomposition\n",[33,2625,2626],{"class":35,"line":314},[33,2627,59],{"emptyLinePlaceholder":58},[33,2629,2630],{"class":35,"line":319},[33,2631,2632],{"class":39},"\u002F\u002F ── When normalization matters ──\n",[33,2634,2635,2637,2640,2642,2644,2647,2649,2652,2654,2656,2659],{"class":35,"line":325},[33,2636,176],{"class":175},[33,2638,2639],{"class":79}," names",[33,2641,182],{"class":175},[33,2643,562],{"class":75},[33,2645,2646],{"class":71},"\"café\"",[33,2648,1033],{"class":75},[33,2650,2651],{"class":71},"\"cafe",[33,2653,2463],{"class":79},[33,2655,92],{"class":71},[33,2657,2658],{"class":75},"];  ",[33,2660,2661],{"class":39},"\u002F\u002F both \"café\" but different code units\n",[33,2663,2664,2666,2669,2671,2673,2676,2679],{"class":35,"line":349},[33,2665,176],{"class":175},[33,2667,2668],{"class":79}," unique",[33,2670,182],{"class":175},[33,2672,679],{"class":175},[33,2674,2675],{"class":200}," Set",[33,2677,2678],{"class":75},"(names);  ",[33,2680,2681],{"class":39},"\u002F\u002F Set has 2 entries (they're not equal!)\n",[33,2683,2684,2686,2689,2691,2693,2695,2698,2700,2702,2705,2707,2710,2712,2714,2716,2719],{"class":35,"line":372},[33,2685,176],{"class":175},[33,2687,2688],{"class":79}," unique2",[33,2690,182],{"class":175},[33,2692,679],{"class":175},[33,2694,2675],{"class":200},[33,2696,2697],{"class":75},"(names.",[33,2699,729],{"class":200},[33,2701,210],{"class":75},[33,2703,2704],{"class":734},"n",[33,2706,738],{"class":175},[33,2708,2709],{"class":75}," n.",[33,2711,2545],{"class":200},[33,2713,210],{"class":75},[33,2715,2550],{"class":71},[33,2717,2718],{"class":75},")));  ",[33,2720,2721],{"class":39},"\u002F\u002F Set has 1 entry (normalized)\n",[33,2723,2724],{"class":35,"line":393},[33,2725,2726],{"class":39},"\u002F\u002F Always normalize before comparison, hashing, or deduplication of Unicode strings.\n",[33,2728,2729],{"class":35,"line":399},[33,2730,59],{"emptyLinePlaceholder":58},[33,2732,2733],{"class":35,"line":404},[33,2734,2735],{"class":39},"\u002F\u002F ── Collation with Intl.Collator (locale-aware sorting) ──\n",[33,2737,2738,2740,2743,2745,2747,2749,2751,2754,2756,2759,2761,2763],{"class":35,"line":410},[33,2739,176],{"class":175},[33,2741,2742],{"class":79}," words",[33,2744,182],{"class":175},[33,2746,562],{"class":75},[33,2748,2646],{"class":71},[33,2750,1033],{"class":75},[33,2752,2753],{"class":71},"\"cafe\"",[33,2755,1033],{"class":75},[33,2757,2758],{"class":71},"\" Café\"",[33,2760,1033],{"class":75},[33,2762,2646],{"class":71},[33,2764,1721],{"class":75},[33,2766,2767,2770,2773,2776],{"class":35,"line":438},[33,2768,2769],{"class":75},"words.",[33,2771,2772],{"class":200},"sort",[33,2774,2775],{"class":75},"();  ",[33,2777,2778],{"class":39},"\u002F\u002F default sort: code unit order (not locale-aware)\n",[33,2780,2781,2783,2785,2787,2790,2792,2795,2797,2799,2802],{"class":35,"line":444},[33,2782,2769],{"class":75},[33,2784,2772],{"class":200},[33,2786,210],{"class":75},[33,2788,2789],{"class":175},"new",[33,2791,682],{"class":75},[33,2793,2794],{"class":200},"Collator",[33,2796,210],{"class":75},[33,2798,690],{"class":71},[33,2800,2801],{"class":75},").compare);  ",[33,2803,2804],{"class":39},"\u002F\u002F locale-aware sort\n",[33,2806,2807],{"class":35,"line":1687},[33,2808,2809],{"class":39},"\u002F\u002F Intl.Collator handles accents, case, and locale-specific ordering rules.\n",[15,2811,2813],{"id":2812},"tips-tricks","💡 Tips & Tricks",[20,2815,2816],{"language":22},[24,2817,2819],{"className":26,"code":2818,"language":22,"meta":28,"style":28},"\u002F\u002F ── .at(-1) for the last character (ES2022 — no .slice(-1) needed) ──\n\"hello\".at(-1);   \u002F\u002F \"o\" — negative index from end\n\"hello\".at(-2);   \u002F\u002F \"l\"\n\u002F\u002F Before .at(): \"hello\".slice(-1) or \"hello\"[\"hello\".length - 1]\n\n\u002F\u002F ── Repeat and padStart\u002FpadEnd ──\n\"ab\".repeat(3);        \u002F\u002F \"ababab\"\n\"5\".padStart(3, \"0\");  \u002F\u002F \"005\" (pad with 0 to length 3)\n\"5\".padEnd(3, \"-\");   \u002F\u002F \"5--\" (pad on the right)\n\"255\".padStart(6, \"0\"); \u002F\u002F \"000255\" (useful for fixed-width formatting)\n\n\u002F\u002F ── Trim and variants ──\n\"  hello  \".trim();       \u002F\u002F \"hello\" (both sides)\n\"  hello  \".trimStart();  \u002F\u002F \"hello  \" (left only, formerly trimLeft)\n\"  hello  \".trimEnd();    \u002F\u002F \"  hello\" (right only, formerly trimRight)\n\n\u002F\u002F ── String to array and back ──\n[...\"hello\"];  \u002F\u002F [\"h\", \"e\", \"l\", \"l\", \"o\"] (iterates by code point)\n\"hello\".split(\"\");  \u002F\u002F [\"h\", \"e\", \"l\", \"l\", \"o\"] (splits by code unit)\n\u002F\u002F Use [...] for Unicode safety (surrogate pairs stay together)\n\n\u002F\u002F ── Multiline string from array ──\nconst lines = [\"line 1\", \"line 2\", \"line 3\"];\nlines.join(\"\\n\");  \u002F\u002F \"line 1\\nline 2\\nline 3\"\n\n\u002F\u002F ── Safe HTML attribute escaping ──\nconst escapeAttr = (str) => str.replace(\u002F[&\u003C>\"']\u002Fg, c => ({\n    \"&\": \"&amp;\", \"\u003C\": \"&lt;\", \">\": \"&gt;\", '\"': \"&quot;\", \"'\": \"&#39;\"\n}[c]));\n",[30,2820,2821,2826,2845,2864,2869,2873,2878,2897,2921,2944,2966,2970,2975,2991,3005,3020,3024,3029,3042,3059,3064,3068,3073,3099,3119,3123,3128,3170,3217],{"__ignoreMap":28},[33,2822,2823],{"class":35,"line":36},[33,2824,2825],{"class":39},"\u002F\u002F ── .at(-1) for the last character (ES2022 — no .slice(-1) needed) ──\n",[33,2827,2828,2830,2832,2834,2836,2838,2840,2842],{"class":35,"line":43},[33,2829,1833],{"class":71},[33,2831,76],{"class":75},[33,2833,2095],{"class":200},[33,2835,210],{"class":75},[33,2837,2035],{"class":175},[33,2839,235],{"class":79},[33,2841,876],{"class":75},[33,2843,2844],{"class":39},"\u002F\u002F \"o\" — negative index from end\n",[33,2846,2847,2849,2851,2853,2855,2857,2859,2861],{"class":35,"line":49},[33,2848,1833],{"class":71},[33,2850,76],{"class":75},[33,2852,2095],{"class":200},[33,2854,210],{"class":75},[33,2856,2035],{"class":175},[33,2858,864],{"class":79},[33,2860,876],{"class":75},[33,2862,2863],{"class":39},"\u002F\u002F \"l\"\n",[33,2865,2866],{"class":35,"line":55},[33,2867,2868],{"class":39},"\u002F\u002F Before .at(): \"hello\".slice(-1) or \"hello\"[\"hello\".length - 1]\n",[33,2870,2871],{"class":35,"line":62},[33,2872,59],{"emptyLinePlaceholder":58},[33,2874,2875],{"class":35,"line":68},[33,2876,2877],{"class":39},"\u002F\u002F ── Repeat and padStart\u002FpadEnd ──\n",[33,2879,2880,2883,2885,2888,2890,2892,2894],{"class":35,"line":89},[33,2881,2882],{"class":71},"\"ab\"",[33,2884,76],{"class":75},[33,2886,2887],{"class":200},"repeat",[33,2889,210],{"class":75},[33,2891,2149],{"class":79},[33,2893,1846],{"class":75},[33,2895,2896],{"class":39},"\u002F\u002F \"ababab\"\n",[33,2898,2899,2902,2904,2907,2909,2911,2913,2916,2918],{"class":35,"line":110},[33,2900,2901],{"class":71},"\"5\"",[33,2903,76],{"class":75},[33,2905,2906],{"class":200},"padStart",[33,2908,210],{"class":75},[33,2910,2149],{"class":79},[33,2912,1033],{"class":75},[33,2914,2915],{"class":71},"\"0\"",[33,2917,905],{"class":75},[33,2919,2920],{"class":39},"\u002F\u002F \"005\" (pad with 0 to length 3)\n",[33,2922,2923,2925,2927,2930,2932,2934,2936,2939,2941],{"class":35,"line":125},[33,2924,2901],{"class":71},[33,2926,76],{"class":75},[33,2928,2929],{"class":200},"padEnd",[33,2931,210],{"class":75},[33,2933,2149],{"class":79},[33,2935,1033],{"class":75},[33,2937,2938],{"class":71},"\"-\"",[33,2940,876],{"class":75},[33,2942,2943],{"class":39},"\u002F\u002F \"5--\" (pad on the right)\n",[33,2945,2946,2949,2951,2953,2955,2957,2959,2961,2963],{"class":35,"line":140},[33,2947,2948],{"class":71},"\"255\"",[33,2950,76],{"class":75},[33,2952,2906],{"class":200},[33,2954,210],{"class":75},[33,2956,2016],{"class":79},[33,2958,1033],{"class":75},[33,2960,2915],{"class":71},[33,2962,1825],{"class":75},[33,2964,2965],{"class":39},"\u002F\u002F \"000255\" (useful for fixed-width formatting)\n",[33,2967,2968],{"class":35,"line":155},[33,2969,59],{"emptyLinePlaceholder":58},[33,2971,2972],{"class":35,"line":160},[33,2973,2974],{"class":39},"\u002F\u002F ── Trim and variants ──\n",[33,2976,2977,2980,2982,2985,2988],{"class":35,"line":166},[33,2978,2979],{"class":71},"\"  hello  \"",[33,2981,76],{"class":75},[33,2983,2984],{"class":200},"trim",[33,2986,2987],{"class":75},"();       ",[33,2989,2990],{"class":39},"\u002F\u002F \"hello\" (both sides)\n",[33,2992,2993,2995,2997,3000,3002],{"class":35,"line":172},[33,2994,2979],{"class":71},[33,2996,76],{"class":75},[33,2998,2999],{"class":200},"trimStart",[33,3001,2775],{"class":75},[33,3003,3004],{"class":39},"\u002F\u002F \"hello  \" (left only, formerly trimLeft)\n",[33,3006,3007,3009,3011,3014,3017],{"class":35,"line":194},[33,3008,2979],{"class":71},[33,3010,76],{"class":75},[33,3012,3013],{"class":200},"trimEnd",[33,3015,3016],{"class":75},"();    ",[33,3018,3019],{"class":39},"\u002F\u002F \"  hello\" (right only, formerly trimRight)\n",[33,3021,3022],{"class":35,"line":222},[33,3023,59],{"emptyLinePlaceholder":58},[33,3025,3026],{"class":35,"line":243},[33,3027,3028],{"class":39},"\u002F\u002F ── String to array and back ──\n",[33,3030,3031,3033,3035,3037,3039],{"class":35,"line":264},[33,3032,1132],{"class":75},[33,3034,565],{"class":175},[33,3036,1833],{"class":71},[33,3038,2658],{"class":75},[33,3040,3041],{"class":39},"\u002F\u002F [\"h\", \"e\", \"l\", \"l\", \"o\"] (iterates by code point)\n",[33,3043,3044,3046,3048,3050,3052,3054,3056],{"class":35,"line":269},[33,3045,1833],{"class":71},[33,3047,76],{"class":75},[33,3049,495],{"class":200},[33,3051,210],{"class":75},[33,3053,500],{"class":71},[33,3055,905],{"class":75},[33,3057,3058],{"class":39},"\u002F\u002F [\"h\", \"e\", \"l\", \"l\", \"o\"] (splits by code unit)\n",[33,3060,3061],{"class":35,"line":275},[33,3062,3063],{"class":39},"\u002F\u002F Use [...] for Unicode safety (surrogate pairs stay together)\n",[33,3065,3066],{"class":35,"line":296},[33,3067,59],{"emptyLinePlaceholder":58},[33,3069,3070],{"class":35,"line":314},[33,3071,3072],{"class":39},"\u002F\u002F ── Multiline string from array ──\n",[33,3074,3075,3077,3080,3082,3084,3087,3089,3092,3094,3097],{"class":35,"line":319},[33,3076,176],{"class":175},[33,3078,3079],{"class":79}," lines",[33,3081,182],{"class":175},[33,3083,562],{"class":75},[33,3085,3086],{"class":71},"\"line 1\"",[33,3088,1033],{"class":75},[33,3090,3091],{"class":71},"\"line 2\"",[33,3093,1033],{"class":75},[33,3095,3096],{"class":71},"\"line 3\"",[33,3098,1721],{"class":75},[33,3100,3101,3104,3106,3108,3110,3112,3114,3116],{"class":35,"line":325},[33,3102,3103],{"class":75},"lines.",[33,3105,512],{"class":200},[33,3107,210],{"class":75},[33,3109,92],{"class":71},[33,3111,95],{"class":79},[33,3113,92],{"class":71},[33,3115,905],{"class":75},[33,3117,3118],{"class":39},"\u002F\u002F \"line 1\\nline 2\\nline 3\"\n",[33,3120,3121],{"class":35,"line":349},[33,3122,59],{"emptyLinePlaceholder":58},[33,3124,3125],{"class":35,"line":372},[33,3126,3127],{"class":39},"\u002F\u002F ── Safe HTML attribute escaping ──\n",[33,3129,3130,3132,3135,3137,3139,3141,3143,3145,3147,3149,3151,3153,3156,3158,3160,3162,3165,3167],{"class":35,"line":393},[33,3131,176],{"class":175},[33,3133,3134],{"class":200}," escapeAttr",[33,3136,182],{"class":175},[33,3138,416],{"class":75},[33,3140,1088],{"class":734},[33,3142,1096],{"class":75},[33,3144,1255],{"class":175},[33,3146,492],{"class":75},[33,3148,1329],{"class":200},[33,3150,210],{"class":75},[33,3152,1334],{"class":71},[33,3154,3155],{"class":79},"[&\u003C>\"']",[33,3157,1334],{"class":71},[33,3159,1343],{"class":175},[33,3161,1033],{"class":75},[33,3163,3164],{"class":734},"c",[33,3166,738],{"class":175},[33,3168,3169],{"class":75}," ({\n",[33,3171,3172,3175,3178,3180,3182,3185,3187,3189,3191,3194,3196,3198,3200,3203,3205,3207,3209,3212,3214],{"class":35,"line":399},[33,3173,3174],{"class":71},"    \"&\"",[33,3176,3177],{"class":75},": ",[33,3179,1348],{"class":71},[33,3181,1033],{"class":75},[33,3183,3184],{"class":71},"\"\u003C\"",[33,3186,3177],{"class":75},[33,3188,1373],{"class":71},[33,3190,1033],{"class":75},[33,3192,3193],{"class":71},"\">\"",[33,3195,3177],{"class":75},[33,3197,1397],{"class":71},[33,3199,1033],{"class":75},[33,3201,3202],{"class":71},"'\"'",[33,3204,3177],{"class":75},[33,3206,1420],{"class":71},[33,3208,1033],{"class":75},[33,3210,3211],{"class":71},"\"'\"",[33,3213,3177],{"class":75},[33,3215,3216],{"class":71},"\"&#39;\"\n",[33,3218,3219],{"class":35,"line":404},[33,3220,3221],{"class":75},"}[c]));\n",[15,3223,3225],{"id":3224},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[20,3227,3228],{"language":22},[24,3229,3231],{"className":26,"code":3230,"language":22,"meta":28,"style":28},"\u002F\u002F ── .length counts code units, not characters ──\n\"😀\".length;  \u002F\u002F 2 (surrogate pair = 2 code units)\n\u002F\u002F Use [...str].length for grapheme count (code points)\n\n\u002F\u002F ── .charAt() and index access break on surrogate pairs ──\n\"😀\".charAt(0);  \u002F\u002F \"\\uD83D\" (high surrogate only — garbage character)\n\"😀\"[0];         \u002F\u002F \"\\uD83D\" (same — code unit, not code point)\n\u002F\u002F Use for...of or [...str] to iterate by code point.\n\n\u002F\u002F ── .substring() swaps args if start > end ──\n\"hello\".substring(4, 1);  \u002F\u002F \"ell\" (swaps to 1,4)\n\"hello\".slice(4, 1);      \u002F\u002F \"\" (no swap — empty)\n\u002F\u002F Use .slice() for consistent behavior.\n\n\u002F\u002F ── .replace() with a string replaces only the FIRST match ──\n\"aaa\".replace(\"a\", \"b\");  \u002F\u002F \"baa\" (first only)\n\"aaa\".replaceAll(\"a\", \"b\");  \u002F\u002F \"bbb\" (ES2021 — all)\n\n\u002F\u002F ── Regex with \u002Fg has stateful lastIndex (for .test() and .exec()) ──\nconst re = \u002Fa\u002Fg;\nre.test(\"aaa\");  \u002F\u002F true (lastIndex = 1)\nre.test(\"aaa\");  \u002F\u002F true (lastIndex = 2)\nre.test(\"aaa\");  \u002F\u002F true (lastIndex = 3)\nre.test(\"aaa\");  \u002F\u002F false (lastIndex = 0, reset — no more matches)\n\u002F\u002F .replace() and .matchAll() reset internally, but .test()\u002F.exec() persist lastIndex.\n\n\u002F\u002F ── Template literals can contain newlines, but watch indentation ──\nconst html = `\n    \u003Cdiv>\n        content\n    \u003C\u002Fdiv>\n`;\n\u002F\u002F The leading whitespace is part of the string — use .trim() or a tag function.\n",[30,3232,3233,3238,3251,3256,3260,3264,3281,3295,3300,3304,3309,3330,3351,3356,3360,3365,3386,3407,3411,3416,3434,3451,3466,3481,3496,3501,3505,3510,3520,3525,3530,3535,3541],{"__ignoreMap":28},[33,3234,3235],{"class":35,"line":36},[33,3236,3237],{"class":39},"\u002F\u002F ── .length counts code units, not characters ──\n",[33,3239,3240,3242,3244,3246,3248],{"class":35,"line":43},[33,3241,143],{"class":71},[33,3243,76],{"class":75},[33,3245,80],{"class":79},[33,3247,188],{"class":75},[33,3249,3250],{"class":39},"\u002F\u002F 2 (surrogate pair = 2 code units)\n",[33,3252,3253],{"class":35,"line":49},[33,3254,3255],{"class":39},"\u002F\u002F Use [...str].length for grapheme count (code points)\n",[33,3257,3258],{"class":35,"line":55},[33,3259,59],{"emptyLinePlaceholder":58},[33,3261,3262],{"class":35,"line":62},[33,3263,272],{"class":39},[33,3265,3266,3268,3270,3272,3274,3276,3278],{"class":35,"line":68},[33,3267,143],{"class":71},[33,3269,76],{"class":75},[33,3271,284],{"class":200},[33,3273,210],{"class":75},[33,3275,213],{"class":79},[33,3277,905],{"class":75},[33,3279,3280],{"class":39},"\u002F\u002F \"\\uD83D\" (high surrogate only — garbage character)\n",[33,3282,3283,3285,3287,3289,3292],{"class":35,"line":89},[33,3284,143],{"class":71},[33,3286,1132],{"class":75},[33,3288,213],{"class":79},[33,3290,3291],{"class":75},"];         ",[33,3293,3294],{"class":39},"\u002F\u002F \"\\uD83D\" (same — code unit, not code point)\n",[33,3296,3297],{"class":35,"line":110},[33,3298,3299],{"class":39},"\u002F\u002F Use for...of or [...str] to iterate by code point.\n",[33,3301,3302],{"class":35,"line":125},[33,3303,59],{"emptyLinePlaceholder":58},[33,3305,3306],{"class":35,"line":140},[33,3307,3308],{"class":39},"\u002F\u002F ── .substring() swaps args if start > end ──\n",[33,3310,3311,3313,3315,3317,3319,3321,3323,3325,3327],{"class":35,"line":155},[33,3312,1833],{"class":71},[33,3314,76],{"class":75},[33,3316,2051],{"class":200},[33,3318,210],{"class":75},[33,3320,2186],{"class":79},[33,3322,1033],{"class":75},[33,3324,235],{"class":79},[33,3326,905],{"class":75},[33,3328,3329],{"class":39},"\u002F\u002F \"ell\" (swaps to 1,4)\n",[33,3331,3332,3334,3336,3338,3340,3342,3344,3346,3348],{"class":35,"line":160},[33,3333,1833],{"class":71},[33,3335,76],{"class":75},[33,3337,1988],{"class":200},[33,3339,210],{"class":75},[33,3341,2186],{"class":79},[33,3343,1033],{"class":75},[33,3345,235],{"class":79},[33,3347,1914],{"class":75},[33,3349,3350],{"class":39},"\u002F\u002F \"\" (no swap — empty)\n",[33,3352,3353],{"class":35,"line":166},[33,3354,3355],{"class":39},"\u002F\u002F Use .slice() for consistent behavior.\n",[33,3357,3358],{"class":35,"line":172},[33,3359,59],{"emptyLinePlaceholder":58},[33,3361,3362],{"class":35,"line":194},[33,3363,3364],{"class":39},"\u002F\u002F ── .replace() with a string replaces only the FIRST match ──\n",[33,3366,3367,3369,3371,3373,3375,3377,3379,3381,3383],{"class":35,"line":222},[33,3368,2235],{"class":71},[33,3370,76],{"class":75},[33,3372,1329],{"class":200},[33,3374,210],{"class":75},[33,3376,72],{"class":71},[33,3378,1033],{"class":75},[33,3380,2248],{"class":71},[33,3382,905],{"class":75},[33,3384,3385],{"class":39},"\u002F\u002F \"baa\" (first only)\n",[33,3387,3388,3390,3392,3394,3396,3398,3400,3402,3404],{"class":35,"line":243},[33,3389,2235],{"class":71},[33,3391,76],{"class":75},[33,3393,2291],{"class":200},[33,3395,210],{"class":75},[33,3397,72],{"class":71},[33,3399,1033],{"class":75},[33,3401,2248],{"class":71},[33,3403,905],{"class":75},[33,3405,3406],{"class":39},"\u002F\u002F \"bbb\" (ES2021 — all)\n",[33,3408,3409],{"class":35,"line":264},[33,3410,59],{"emptyLinePlaceholder":58},[33,3412,3413],{"class":35,"line":269},[33,3414,3415],{"class":39},"\u002F\u002F ── Regex with \u002Fg has stateful lastIndex (for .test() and .exec()) ──\n",[33,3417,3418,3420,3422,3424,3426,3428,3430,3432],{"class":35,"line":275},[33,3419,176],{"class":175},[33,3421,2325],{"class":79},[33,3423,182],{"class":175},[33,3425,2330],{"class":71},[33,3427,2269],{"class":1337},[33,3429,1334],{"class":71},[33,3431,1343],{"class":175},[33,3433,806],{"class":75},[33,3435,3436,3439,3442,3444,3446,3448],{"class":35,"line":296},[33,3437,3438],{"class":75},"re.",[33,3440,3441],{"class":200},"test",[33,3443,210],{"class":75},[33,3445,2235],{"class":71},[33,3447,905],{"class":75},[33,3449,3450],{"class":39},"\u002F\u002F true (lastIndex = 1)\n",[33,3452,3453,3455,3457,3459,3461,3463],{"class":35,"line":314},[33,3454,3438],{"class":75},[33,3456,3441],{"class":200},[33,3458,210],{"class":75},[33,3460,2235],{"class":71},[33,3462,905],{"class":75},[33,3464,3465],{"class":39},"\u002F\u002F true (lastIndex = 2)\n",[33,3467,3468,3470,3472,3474,3476,3478],{"class":35,"line":319},[33,3469,3438],{"class":75},[33,3471,3441],{"class":200},[33,3473,210],{"class":75},[33,3475,2235],{"class":71},[33,3477,905],{"class":75},[33,3479,3480],{"class":39},"\u002F\u002F true (lastIndex = 3)\n",[33,3482,3483,3485,3487,3489,3491,3493],{"class":35,"line":325},[33,3484,3438],{"class":75},[33,3486,3441],{"class":200},[33,3488,210],{"class":75},[33,3490,2235],{"class":71},[33,3492,905],{"class":75},[33,3494,3495],{"class":39},"\u002F\u002F false (lastIndex = 0, reset — no more matches)\n",[33,3497,3498],{"class":35,"line":349},[33,3499,3500],{"class":39},"\u002F\u002F .replace() and .matchAll() reset internally, but .test()\u002F.exec() persist lastIndex.\n",[33,3502,3503],{"class":35,"line":372},[33,3504,59],{"emptyLinePlaceholder":58},[33,3506,3507],{"class":35,"line":393},[33,3508,3509],{"class":39},"\u002F\u002F ── Template literals can contain newlines, but watch indentation ──\n",[33,3511,3512,3514,3516,3518],{"class":35,"line":399},[33,3513,176],{"class":175},[33,3515,946],{"class":79},[33,3517,182],{"class":175},[33,3519,951],{"class":71},[33,3521,3522],{"class":35,"line":404},[33,3523,3524],{"class":71},"    \u003Cdiv>\n",[33,3526,3527],{"class":35,"line":410},[33,3528,3529],{"class":71},"        content\n",[33,3531,3532],{"class":35,"line":438},[33,3533,3534],{"class":71},"    \u003C\u002Fdiv>\n",[33,3536,3537,3539],{"class":35,"line":444},[33,3538,986],{"class":71},[33,3540,806],{"class":75},[33,3542,3543],{"class":35,"line":1687},[33,3544,3545],{"class":39},"\u002F\u002F The leading whitespace is part of the string — use .trim() or a tag function.\n",[15,3547,3549],{"id":3548},"quick-quiz","🧠 Quick Quiz",[3551,3552,3553],"p",{},"What does this output?",[20,3555,3556],{"language":22},[24,3557,3559],{"className":26,"code":3558,"language":22,"meta":28,"style":28},"const str = \"𝕏\";  \u002F\u002F U+1D54F (mathematical double-struck capital X)\nconsole.log(str.length);\nconsole.log([...str].length);\nconsole.log(str.charAt(0) === str[0]);\nconsole.log(str.codePointAt(0));\n",[30,3560,3561,3577,3590,3607,3633],{"__ignoreMap":28},[33,3562,3563,3565,3567,3569,3572,3574],{"class":35,"line":36},[33,3564,176],{"class":175},[33,3566,470],{"class":79},[33,3568,182],{"class":175},[33,3570,3571],{"class":71}," \"𝕏\"",[33,3573,188],{"class":75},[33,3575,3576],{"class":39},"\u002F\u002F U+1D54F (mathematical double-struck capital X)\n",[33,3578,3579,3581,3583,3586,3588],{"class":35,"line":43},[33,3580,197],{"class":75},[33,3582,201],{"class":200},[33,3584,3585],{"class":75},"(str.",[33,3587,80],{"class":79},[33,3589,519],{"class":75},[33,3591,3592,3594,3596,3599,3601,3603,3605],{"class":35,"line":49},[33,3593,197],{"class":75},[33,3595,201],{"class":200},[33,3597,3598],{"class":75},"([",[33,3600,565],{"class":175},[33,3602,568],{"class":75},[33,3604,80],{"class":79},[33,3606,519],{"class":75},[33,3608,3609,3611,3613,3615,3617,3619,3621,3623,3625,3628,3630],{"class":35,"line":55},[33,3610,197],{"class":75},[33,3612,201],{"class":200},[33,3614,3585],{"class":75},[33,3616,284],{"class":200},[33,3618,210],{"class":75},[33,3620,213],{"class":79},[33,3622,1096],{"class":75},[33,3624,1282],{"class":175},[33,3626,3627],{"class":75}," str[",[33,3629,213],{"class":79},[33,3631,3632],{"class":75},"]);\n",[33,3634,3635,3637,3639,3641,3643,3645,3647],{"class":35,"line":62},[33,3636,197],{"class":75},[33,3638,201],{"class":200},[33,3640,3585],{"class":75},[33,3642,252],{"class":200},[33,3644,210],{"class":75},[33,3646,213],{"class":79},[33,3648,3649],{"class":75},"));\n",[3651,3652,3653,3657,3692],"details",{},[3654,3655,3656],"summary",{},"Answer",[24,3658,3660],{"className":26,"code":3659,"language":22,"meta":28,"style":28},"2          \u002F\u002F str.length — 2 code units (surrogate pair: U+D835, U+DD4F)\n1          \u002F\u002F [...str].length — 1 code point (spread iterates by code point)\ntrue       \u002F\u002F str.charAt(0) === str[0] — both return the high surrogate \"\\uD835\"\n120143     \u002F\u002F str.codePointAt(0) — 0x1D54F = 120143 (the actual code point)\n",[30,3661,3662,3669,3676,3684],{"__ignoreMap":28},[33,3663,3664,3666],{"class":35,"line":36},[33,3665,864],{"class":79},[33,3667,3668],{"class":39},"          \u002F\u002F str.length — 2 code units (surrogate pair: U+D835, U+DD4F)\n",[33,3670,3671,3673],{"class":35,"line":43},[33,3672,235],{"class":79},[33,3674,3675],{"class":39},"          \u002F\u002F [...str].length — 1 code point (spread iterates by code point)\n",[33,3677,3678,3681],{"class":35,"line":49},[33,3679,3680],{"class":79},"true",[33,3682,3683],{"class":39},"       \u002F\u002F str.charAt(0) === str[0] — both return the high surrogate \"\\uD835\"\n",[33,3685,3686,3689],{"class":35,"line":55},[33,3687,3688],{"class":79},"120143",[33,3690,3691],{"class":39},"     \u002F\u002F str.codePointAt(0) — 0x1D54F = 120143 (the actual code point)\n",[3551,3693,3694,3698,3699,1033,3702,3705,3706,3709,3710,3712,3713,3716,3717,3720,3721,3724],{},[3695,3696,3697],"strong",{},"The lesson",": JavaScript strings are UTF-16 — ",[30,3700,3701],{},".length",[30,3703,3704],{},".charAt()",", and index access operate on ",[3695,3707,3708],{},"code units"," (16-bit), not code points. Characters outside the BMP (U+0000–U+FFFF) are encoded as surrogate pairs (2 code units), so ",[30,3711,3701],{}," returns 2 for a single emoji. Use ",[30,3714,3715],{},"[...str]"," or ",[30,3718,3719],{},"for...of"," to iterate by code point, and ",[30,3722,3723],{},".codePointAt()"," for the actual Unicode code point.",[3726,3727,3728],"style",{},"html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}html pre.shiki code .svAP2, html code.shiki .svAP2{--shiki-default:#032F62;--shiki-github-dark:#DBEDFF}",{"title":28,"searchDepth":43,"depth":43,"links":3730},[3731,3732,3733,3734,3735,3736,3737,3738,3739],{"id":17,"depth":43,"text":18},{"id":450,"depth":43,"text":451},{"id":778,"depth":43,"text":779},{"id":1189,"depth":43,"text":1190},{"id":1776,"depth":43,"text":1777},{"id":2398,"depth":43,"text":2399},{"id":2812,"depth":43,"text":2813},{"id":3224,"depth":43,"text":3225},{"id":3548,"depth":43,"text":3549},"Deep-dive into JavaScript string mechanics: UTF-16 code units vs code points, surrogate pair handling, Unicode normalization, tagged template literals, and the string iterator vs index access. Code-first reference for senior engineers.","md",{},"\u002Fjs\u002F06-strings-and-template-literals",{"title":5,"description":3740},"js\u002F06-strings-and-template-literals","7EybzdhYLe75UftcltMrmfh8GlCwPBH-rKx6fO2XeKs",1789924649084]