Strings in JavaScript

The String data type in JavaScript is used to represent a sequence of zero or more 16-bit Unicode characters. Strings can be created using double quotes ("), single quotes ('), or backticks (`). So, you can write strings like this:

let favoriteFood = "pizza";
let favoriteDrink = 'lemonade';
let favoriteActivity = `swimming`;

Unlike some languages in which using different quotes changes how the string is interpreted, there is no difference in the syntaxes in JavaScript. However, it's important to remember that a string must start and end with the same type of quote.



Character Literals

The String data type in JavaScript has special character literals that represent nonprintable or useful characters. These characters are listed in the following table:

LITERAL MEANING
\n New line
\t Tab
\b Backspace
\r Carriage return
\f Form feed
\\\\ Backslash ( \ )
\' Single quote ( ' ) - used when the string is enclosed by single quotes. For example: 'He said, \'hey.\''
\" Double quote ( " ) - used when the string is enclosed by double quotes. For example: "He said, \"hey.\""
\` Backtick ( ` ) - used when the string is enclosed by backticks. For example: `He said, \`hey.\``
\xnn A character represented by hexadecimal code nn (where n is a hexadecimal digit 0-F). Example: \x41 is equivalent to "A".
\unnnn A Unicode character represented by the hexadecimal code nnnn (where n is a hexadecimal digit 0-F). Example: \u03a3 is equivalent to the Greek character Σ


These special character literals can be placed anywhere within a string and will be treated as a single character. For instance:

let text = "This is the letter sigma: \u03a3."

In this example, the variable text contains 28 characters, even though the escape sequence is 6 characters long. The escape sequence represents one character and is counted as such.

You can find the length of any string using the length property like this:

console.log(text.length); // 28

The length property gives the number of 16-bit characters in the string.



Strings are immutable

In JavaScript, strings are considered immutable, which means that once a string is created, it cannot be changed or modified directly. Instead of modifying the existing string, any operation that appears to change the string actually creates a new string with the desired modifications while leaving the original string unchanged.

let message = "Hello, World!"; // Original string

// Trying to change the first character of the string (Doesn't work)
message[0] = "h";

// The original string remains unchanged
console.log(message); // Output: "Hello, World!"

// Creating a new string with the desired change
let newMessage = "h" + message.slice(1);

console.log(newMessage); // Output: "hello, World!"

In the example above, we attempt to change the first character of the message string to a lowercase "h" using message[0] = "h";. However, JavaScript won't allow this direct modification, and the original string remains unchanged. Instead, we create a new string newMessage by combining the lowercase "h" with the rest of the message using message.slice(1) to remove the first character. This way, we get the desired result without altering the original string.

This immutability property is essential in JavaScript as it ensures the consistency and reliability of strings throughout the program. It helps avoid unintentional changes to data and makes string operations safer and more predictable. Whenever you need to modify a string, you should create a new one with the necessary changes rather than attempting to modify the original string directly.



Converting to a String

There are two ways to convert a value to a string. One way is by using the toString() method, which most values have. This method simply returns the string representation of the value. Here's an example:

let number = 123;
let numberString = number.toString(); // the string "123"

let value = true;
let valueString = value.toString(); // the string "true"

The toString() method is available for numbers, Booleans, objects, and strings. Each string also has its own toString() method that returns a copy of itself. However, if a value is null or undefined, the toString() method is not available for that value.

In most cases, the toString() method doesn't require any arguments. However, when used with a number, toString() can take a single argument called the 'radix', which determines how the number will be represented. By default, toString() returns the number in decimal format. But when you provide a radix, you can get the number's representation in binary, octal, hexadecimal, or any other valid base, as shown in this example:

let num = 10;
console.log(num.toString());    // "10"
console.log(num.toString(2));   // "1010"
console.log(num.toString(8));   // "12"
console.log(num.toString(10));  // "10"
console.log(num.toString(16));  // "a"

If you're unsure whether a value is null or undefined, you can use the String() casting function. It always returns a string, regardless of the original value's type. The String() function follows these rules:

  • If the value has a toString() method, it is called (with no arguments) and the result is returned.
  • If the value is null, "null" is returned.
  • If the value is undefined, "undefined" is returned.

Consider the following:

let value1 = 10;
let value2 = true;
let value3 = null;
let value4;

console.log(String(value1));    // "10"
console.log(String(value2));    // "true"
console.log(String(value3));    // "null"
console.log(String(value4));    // "undefined"

You can also convert a value to a string by adding an empty string ("") to that value using the plus operator

let number = 123;
let numberString = "" + number; 
console.log(numberString);  // "123"


Template Literals

Template literals provide a more flexible and powerful way to work with strings in JavaScript. They are enclosed by backticks ( ` ) instead of the traditional single ( ' ) or double ( " ) quotes.

Template literals in JavaScript have the unique ability to preserve whitespace and new line characters, making them a convenient choice for creating multiline strings. When you define a string using template literals, any whitespace, including tabs, spaces, and line breaks, is retained as part of the string:

let multiLineString = `first line\nsecond line`;
let multiLineTemplateLiteral = `first line
second line`; console.log(multiLineString); // first line // second line console.log(multiLineTemplateLiteral); // first line // second line console.log(multiLineString === multiLineTemplateLiteral); // true

As the name suggests, template literals are especially useful when defining templates, such as HTML:

let htmlTemplate = `
<div>
    <h2>Hello World!</h2>
</div>`;



Interpolation

Template literals have a valuable feature called interpolation, which lets you insert values at one or more locations within a single unbroken definition. Unlike regular strings, template literals are special JavaScript expressions that are immediately evaluated and converted into string instances. When you define a template literal, any variables you interpolate will be taken from its immediate scope.

This can be accomplished using a JavaScript expression inside ${}:

let value = 5;
let exponent = 'second';

let interpolated = `${ value } to the ${ exponent } power is ${ value * value }`;

console.log(interpolated); // 5 to the second power is 25

In template literals, the values being interpolated will be automatically converted to strings using toString(). You can safely interpolate any JavaScript expression without worries. Nesting template literals is also safe and doesn't require any escaping:

console.log(`Hello, ${ `World` }!`); // Hello, World!

toString() is invoked to coerce expression result into string:

let foo = { toString: () => 'World' };
console.log(`Hello, ${ foo }!`); // Hello, World!


Invoking functions and methods inside interpolated expressions is allowed:

function capitalize(word) {
    return `${ word[0].toUpperCase() }${ word.slice(1) }`;
}

console.log(`${ capitalize('hello') }, ${ capitalize('world') }!`); // Hello, World!

Additionally, templates can safely interpolate their previous value:

let value = '';
function append() {
    value = `${value}abc`
    console.log(value);
}

append(); // abc
append(); // abcabc
append(); // abcabcabc


Tag Functions

Tag functions are a unique feature in JavaScript that allows you to customize the behavior of template literals. When you use a template literal with a tag function, the function gets invoked with the processed parts of the template literal as its arguments. The tag function can then manipulate and combine these parts to create the final result.

A tag function is defined as a regular function and is applied to a template literal by being prepended to it:

tagFunction`template literal parts`

The tag function will be passed the template literal split into its pieces: the first argument is an array of the raw strings, and the remaining arguments are the results of the evaluated expressions:

function tagFunction(strings, ...values) {
    // strings: an array of string literals
    // values: the interpolated values as separate arguments
    // ... (rest parameter syntax) allows capturing multiple values in an array
    // You can process and combine the strings and values as needed
    // and return the customized result.
}

The return value of this function will be the string evaluated from the template literal.

Example:

function highlight(strings, ...values) {
    let result = '';
    strings.forEach((string, i) => {
        result += string;
        if (values[i]) {
            result += `<strong>${values[i]}</strong>`;
        }
    });
    return result;
}

const name = "Alice";
const age = 25;

const message = highlight`Hello, my name is ${name} and I am ${age} years old.`;

console.log(message);
// Hello, my name is <strong>Alice</strong> and I am <strong>25</strong> years old.

In this example, we define a highlight tag function. The tag function takes the parts of the template literal as separate arguments (strings and values) and customizes the output. In this case, the highlight function adds <strong> tags around the interpolated values to highlight them.


When you use a tag function with a template literal containing n interpolated values, the tag function will receive n expression arguments and n + 1 string pieces as its first argument. You can combine the strings and evaluated expressions into the default returned string by following this approach:

function zipTag(strings, ...expressions) {
    return strings[0] +
        expressions.map((e, i) => `${e}${strings[i + 1]}`)
        .join('');
}

const name = "Alice";
const age = 25;

const message = zipTag`Hello, my name is ${name} and I am ${age} years old.`;

console.log(message);
// Hello, my name is Alice and I am 25 years old.

Tag functions are a powerful tool for customizing template literals and can be used for various purposes, such as localization, escaping, syntax highlighting, and more.



Raw Strings

Raw strings, also known as raw template strings, are a feature in JavaScript that allows you to create strings without interpreting escape sequences. Unlike regular template strings, raw template strings preserve backslashes ( \ ) as-is and do not treat them as escape characters. They are particularly useful when you need to work with strings that contain literal backslashes, such as file paths or regular expressions.

String.raw is a built-in tag function in JavaScript used with raw template strings. As a tag function, it takes the template string parts and interpolated values as arguments and returns a raw string without processing escape sequences. When you use String.raw, it's as if you wrote a raw template string directly.

Syntax:

String.raw(callSite, ...substitutions)

Example:

const path = String.raw`C:\Users\Username\Documents\file.txt`;
console.log(path); // C:\Users\Username\Documents\file.txt

In this example, we use String.raw with a raw template string to create the path variable. The raw string preserves the backslashes as-is, resulting in the correct file path representation.


The raw property is a property available on each element in the string piece collection inside the tag function. The raw property returns an array containing the raw strings, i.e., the strings with escape sequences still intact, exactly as they were written.

Example:

function myTag(strings, ...values) {
    console.log(strings.raw);
}

myTag`Hello\nWorld`; // Output: Array [ "Hello\\nWorld" ]


In this example, we define the myTag tag function, which accesses the raw strings using the raw property. When we call myTag with the template literal Hello\nWorld, the output shows that the \n escape sequence is preserved as \\n in the raw array.

Raw strings, the String.raw tag function, and the raw property provide a convenient way to work with strings containing backslashes and escape sequences without any additional processing. They are particularly useful when dealing with certain types of content that require literal backslashes, such as file paths and regular expressions.