Unicode and escape sequences

Unicode

Computers work internally with bits, represented as either 1 or 0. Characters as used in texts must be "translated" into sequences of bits, and vice versa. This is called character encoding. Character encoding uses a certain standard "encoding table". The dominant standard for consistent character encoding is Unicode. Unicode defines several transformation formats, including UTF-8 and UTF-16. The first Unicode characters in the "encoding table" are ASCII characters: the characters on a standard USA keyboard. Unicode extends the set of ASCII massively, covering characters from most alphabets, symbols, emoji, etc.

In Unicode the sequence of bits (a sequence of zeros and ones), associated with a character, is interpreted as a number. Such a number is called a code point. Code points are denoted as "U+" followed by the code point value represented as a hexadecimal number. For instance, U+03C9 is the code point for the Greek Small letter Omega (ω) and U+23F0 is the code point for an alarm clock character (⏰). This Wikipedia page provides a list of the most important character code points for English-language readers. This Wikipedia page provides a list of emoji code points 😵.

In JavaScript Unicode characters can be included in a string by simply typing them on a keyboard, copy-pasting them from an other text, using a special character picker (⊞ Win+. or Control ⌃+Command ⌘+Spacebar) etc. They can also be included by using escape sequences. The general escape sequences for Unicode code points in JavaScript is: \u{X...XXXXXX} where X...XXXXXX are 1 to 6 hexadecimal digits being the code point value.


console.log("\u{1F4A9}\u{0020}\u{0069}\u{0073}\u{0020}\u{0061}\u{0020}\u{0070}\u{0069}\u{006C}\u{0065}\u{0020}\u{006F}\u{0066}\u{0020}\u{0070}\u{006F}\u{006F}\u{002E}");

// logs: "💩 is a pile of poo."

JavaScript also provides shorter escape sequences for subsets of Unicode code points:

Escape sequences \u{0000A3}, \u00A3 and \xA3 all represent the same code point.


console.log("\u23F0 is an alarm clock and \u03C9 is the Greek Small letter Omega.");
// logs: "⏰ is an alarm clock and ω is the Greek Small letter Omega."

console.log("\xA5 is the currency sign for the Japanese yen and \xA3 is the currency sign for the British pound.");
// logs: "¥ is the currency sign for the Japanese yen and £ is the currency sign for the British pound."

Whitespace and line terminators

Also characters that cannot be printed, like tabs, backspace, or a line feed have Unicode code points. In many formal languages, including regular expressions and JavaScript, these characters can be denoted by shorthand single character escape sequences.

In the next code sample a tab Unicode escape sequence (\u{0009}), a tab shorthand single character escape sequence (\t) and a tab by simply typing a tab via the Tab key on the keyboard are included in a string. All three do the same thing.


console.log("Next character\u{0009}is a tab."); // logs: "Next character	is a tab."
console.log("Next character\tis a tab."); // logs: "Next character	is a tab."
console.log("Next character	is a tab."); // logs: "Next character	is a tab."

See this list of escape sequences in JavaScript .

The "line terminators" carriage return, line feed and newline are closely associated:

Carriage return (U+000D):
Escape sequence: \r.
Return the cursor to the beginning of the current line without advancing downward.
Line feed (U+000A):
Escape sequence: \n.
Advance the cursor downward to the next line without returning to the beginning of the line.
Newline:
Escape sequence: \r\n.
A "next line" or a "line break": a carriage return and a line feed combined. Equivalent to pressing the ↵ Enter key.

Although historically line terminators are defined as listed above, various programming languages treat them in various ways. JavaScript treats \n (or \u{000A}) as a newline instead of a line feed. In JavaScript escape sequence \r\n serves as a redundant carriage return and a newline.

In JavaScript, a newline is equivalent to pressing the ↵ Enter key, although in a string literal an unescaped newline throws a SyntaxError. You can insert a newline escape sequence \n to insert an actual newline.


console.log("This is line 1
and this is line 2");
  // logs: SyntaxError: "" string literal contains an unescaped line break

console.log("This is line 1 \nand this is line 2");
  // logs:
  // "This is line 1 
  //  and this is line 2"

In a template literal an unescaped newline (↵ Enter key) is allowed and inserts an actual newline.


console.log(`This is line 1
and this is line 2`);

  // logs:
  // "This is line 1 
  //  and this is line 2"

console.log(`
` === "\n"); // logs: true

console.log(`
` === "\r"); // logs: false

console.log(`
` === "\r\n"); // logs: false

Escaping single characters

Quotation marks (", ') have special meaning in a string literal. They are not part of the actual string characters. They begin and end a string literal. The same thing holds for backslashes (\); they begin an escape sequences. They are not visible as such in the returned string. Escaping quotation marks (\', \") or the backslash (\\) turn them into string characters. Now the escaped character is taken literally as the character itself. This way quotation marks can be used in a string without ending the string and literally a backslash can be added to a string, e.g. to show an escape sequence.


console.log("This is what they call a \"string\", right?"); // logs: "This is what they call a "string", right?"
console.log('This is what they call a \'string\', right?'); // logs: "This is what they call a 'string', right?"

console.log("\\u{1F4A9} is the escape sequences for a pile of poo."); // logs: "\u{1F4A9} is the escape sequences for a pile of poo."

So, some characters escaped with a backslash are interpreted as escape sequences (\n, \t, \u, \x etc.). Any other character escaped with a backslash is interpreted as literally just that single character.


console.log("\H\e\l\l\o" === "Hello"); // logs: true
console.log("'" === "\'"); // logs: true
console.log("\Y\o\u"); // logs: SyntaxError: malformed Unicode character escape sequence // \u starts an escape sequence.
console.log("\Y\o\\u"); // logs: "Yo\u"

An escaped zero (\0), when not followed by a digit between 0 and 7, is interpreted as the null character (U+0000). In JavaScript this character (\0 or \u{0000}) has no meaning. In other languages it sometimes ends a string value.

Note that HTML and CSS each use their own different "escape sequence syntax" to include special Unicode characters. And HTML has its own (non-Unicode) representations for line termination and paragraph control.

When to use escape sequences?

When you write code (HTML, CSS, JavaScript), the text editor you write your code in should be set to encode in UTF-8, because this is the preferred encoding for the web. By far the most common encoding used on the World Wide Web is UTF-8 (> 98% of all web pages, as of 2021). In UTF-8 all Unicode characters can be encoded, so generally you can directly insert any Unicode character in your code. Only newline escape sequences must be escaped in string literals (see above). However, you can use Unicode escape sequences to insert any character. Keep in mind though that using escape sequences can make code more difficult to read and maintain, and also generally increase the file size (it includes more characters).


console.log("\x48\x65\x6C\x6C\x6F" === "Hello"); // logs: true

console.log("ω" === "\u03C9"); // logs: true
console.log("⏰ is an alarm clock and 💩 is a pile of poo."); // logs: "⏰ is an alarm clock and 💩 is a pile of poo."

Surrogate pairs

As mentioned before, all characters in plane 0, aka the Basic Multilingual Plane (BMP), can be escaped by using the format \uXXXX. They use a 16 bits representation, which is called a code unit. Historically in JavaScript every single code unit of 16 bits within a string represents one character.

But characters in the "higher" planes of Unicode ( "supplementary planes"), including mathematical symbols and emoji, have a code point value of 1 to 6 hexadecimal digits (U+XXXXXX). They need more than 16 bits up to 21 bits! To circumvent this, Unicode provides code point pairs within plane 0 to form references to characters of higher planes. These code point pairs are called surrogate pairs. A code point from the range U+D800U+DBFF combined with one from the range U+DC00–U+DFFF addresses a character from a supplementary plane. The surrogate code points on their own have no meaning. Note that you can (and should) use the supplementary planes code points (using the \u{XXXXXX} format): surrogate pairs are used "under the hood".

A consequence is that these supplementary planes characters are represented by two code units, while every code unit represents a character in a string. This means, for example, that methods of the string wrapper object (see next chapter), like for instance length, slice() or substring(), may not work as expected when the string contains characters from supplementary planes.


console.log('\u{1F600}' === '😀'); // logs: true

console.log('😀' === '\uD83D\uDE00'); //  logs: true // two surrogate code points.   
console.log('😀'.length); // logs: 2  // One character but a length of 2...

let str = "😀 is a smiley";
console.log(str[0]); // logs: "�" // unprintable code unit
console.log(str[1]); // logs: "�" // unprintable code unit
console.log(str[3]); // logs: "i"
console.log(str[4]); // logs: "s"
// etc.

As a work-around we can use the Unicode-aware Array.from() method to create an array with each character (and not each code unit) as one element.


let smiley = "\u{1F600}";
let smileyArray = Array.from(smiley);
console.log(smileyArray.length); // logs: 1
console.log(smileyArray[0]); // logs: "😀"

Assembled characters 🤷🏾‍♀️

Unicode characters may also be assembled by putting together multiple code points:

All these Unicode sequence will be discussed in more detail in regular expressions. More sophisticated methods than Array.from() to distinguish the separate characters in a string, will be covered there as well. Array.from() is only aware of surrogate pairs, not of assembled characters.


let str, strArray;

str = "ä"; // "a\u{0308}" (There is also a code point for the whole character: "ä", "\u{00E4}")
strArray = Array.from(str);
console.log(str); // logs: "ä"
console.log(strArray); // logs: [ "a", "̈" ]
console.log(strArray.length); // logs: 2

str = "\u{1F467}+\u{1F3FF}→\u{1F467}\u{1F3FF}";
strArray = Array.from(str);
console.log(str); // logs: "👧+🏿→👧🏿"
console.log(strArray); // logs: [ "👧", "+", "🏿", "→", "👧", "🏿" ]
console.log(strArray.length); // logs: 6

str = "\u{1F468}+\u{1F469}+\u{1F467}→\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F467}";
strArray = Array.from(str);
console.log(str); // logs: "👨+👩+👧→👨‍👩‍👧"
console.log(strArray); // logs: [ "👨", "+", "👩", "+", "👧", "→", "👨", "‍", "👩", "‍", "👧" ]
console.log(strArray.length); // logs: 11

BTW: Emoji sequences may not always display as one would expect because of possible incomplete implementation in browsers. The level of implementation varies from browser to browser as well as the way the emoji are presented.