String Basics
A string is JavaScript’s type for text — a sequence of characters used for everything from user names and URLs to JSON payloads and HTML markup. Strings feel simple, but a few core facts shape how you work with them every day: they can be written with three different kinds of quotes, they are immutable, and each character lives at a zero-based index. This page covers creating strings, reading their length and individual characters, escaping special characters, and joining strings together.
Creating strings
You can write a string literal with single quotes, double quotes, or backticks (template literals). All three produce the same string primitive — the choice is mostly about which quote characters you need to embed without escaping.
const single = 'hello';
const double = "hello";
const backtick = `hello`;
console.log(single === double, double === backtick);
Output:
true true
Single and double quotes are interchangeable; pick one and stay consistent (most style guides and formatters default to one or the other). Backticks unlock interpolation and multi-line text, which are covered in detail on the template literals page.
| Quote style | Multi-line | Interpolation | Typical use |
|---|---|---|---|
'single' | No | No | Default for plain text |
"double" | No | No | When the text contains apostrophes |
`backtick` | Yes | Yes (${}) | Dynamic or multi-line strings |
You can also wrap any value with the String() function to convert it to text. Avoid new String() — that creates a wrapper object, not a primitive, and behaves surprisingly with typeof and ===.
console.log(String(42)); // "42"
console.log(typeof String(42)); // "string"
console.log(typeof new String("x")); // "object" ← avoid
Immutability
Strings are immutable: once created, the characters inside a string can never be changed. Methods like toUpperCase(), slice(), or replace() do not modify the original — they return a brand-new string. Assigning to an index simply does nothing (it throws in strict mode for some hosts, but is silently ignored otherwise).
let greeting = "hello";
greeting[0] = "H"; // ignored — strings are immutable
console.log(greeting); // still "hello"
greeting = greeting.toUpperCase(); // reassign the variable
console.log(greeting); // "HELLO"
Output:
hello
HELLO
The variable can be reassigned, but the string value itself never mutates. To “edit” a string you build a new one and store it back. This is why chaining string methods is safe — each call hands you a fresh string.
Length
Every string has a read-only length property reporting the number of UTF-16 code units it contains. For plain ASCII and most everyday text, that equals the number of characters you see.
console.log("hello".length); // 5
console.log("".length); // 0
console.log(" ".length); // 2 (spaces count)
Output:
5
0
2
lengthcounts code units, not always visual characters. Emoji and other characters above the basic plane take two code units, so"😀".lengthis2. See the Unicode strings page when you need true character counts.
Accessing characters
There is no dedicated character type in JavaScript — a single character is just a string of length 1. You read characters by zero-based index using either bracket notation or the at() method.
const word = "code";
console.log(word[0]); // "c"
console.log(word.charAt(1)); // "o"
console.log(word.at(-1)); // "e" ← ES2022, counts from the end
console.log(word[99]); // undefined (out of range)
Output:
c
o
e
undefined
Bracket notation does not accept negative indexes (word[-1] is undefined), so reach for at(-1) to grab the last character. charAt() is the older equivalent of bracket access and returns an empty string "" for out-of-range indexes instead of undefined.
Escape sequences
Inside quoted strings, a backslash (\) starts an escape sequence that represents a character you can’t type directly — a newline, a tab, or the same quote that delimits the string.
const quote = 'She said "it\'s fine"';
const lines = "line one\nline two";
const tabbed = "name:\tValue";
const path = "C:\\Users\\dev";
console.log(quote);
console.log(lines);
console.log(tabbed);
console.log(path);
Output:
She said "it's fine"
line one
line two
name: Value
C:\Users\dev
Common escapes:
| Sequence | Meaning |
|---|---|
\n | Newline |
\t | Tab |
\\ | Literal backslash |
\' \" | Quote matching the delimiter |
\u{1F600} | Unicode code point (ES2015) |
\` ${ | Backtick / interpolation inside template literals |
Concatenation
The classic way to join strings is the + operator. When either operand is a string, + coerces the other to a string and concatenates.
const first = "Ada";
const last = "Lovelace";
const full = first + " " + last;
console.log(full);
console.log("Score: " + 100); // number coerced to "100"
Output:
Ada Lovelace
Score: 100
For anything beyond trivial joins, template literals are clearer than a chain of + signs — they read like the final string and handle interpolation, multi-line text, and embedded expressions without escaping. You can also use concat() or Array.prototype.join() when assembling many pieces.
const items = ["a", "b", "c"];
console.log(items.join(", ")); // "a, b, c"
console.log("foo".concat("bar")); // "foobar"
Best Practices
- Pick one quote style for plain strings and let your formatter enforce it.
- Reach for template literals over
+whenever you interpolate values or span multiple lines. - Remember strings are immutable — capture the return value of methods; they never edit in place.
- Use
at(-1)for the last character; bracket notation can’t take negative indexes. - Treat
lengthas code units, not always visual characters, when handling emoji or non-BMP text. - Avoid
new String(); use theString()function or a literal to get a real string primitive. - Escape only what you must — switching quote style often removes the need for
\'or\".