Strings and template literals
Names, addresses, pincodes, phone numbers, anything typed into a form, and every response from an API before you parse it — all text. Strings are the type you will handle most, so it is worth knowing more than how to print one.
Three kinds of quote
const single = 'Priya';
const double = "Priya";
const backtick = `Priya`;
Single and double quotes are identical in behaviour. Pick one and be consistent — most JavaScript uses single quotes, and this course does.
Backticks are different, and they are the useful ones.
Template literals
A backtick string can have values dropped into it with ${...}:
const customer = 'Priya';
const plates = 3;
const rate = 80;
console.log(`${customer} ordered ${plates} plates — ₹${plates * rate}`);
Priya ordered 3 plates — ₹240
Anything can go inside ${...}, including arithmetic, as above. Compare the
alternative with +:
console.log(customer + ' ordered ' + plates + ' plates — ₹' + plates * rate);
Same result, harder to read, and one misplaced space away from a bug. Use template literals by default.
They also span lines, which ordinary quotes cannot:
const note = `Order for Priya
Delivery after 7pm
Ring the bell twice`;
The line breaks are part of the string.
Escaping
Sometimes you need a character the quotes would otherwise eat:
| Escape | Meaning |
|---|---|
\n |
New line |
\t |
Tab |
\\ |
A literal backslash |
\' |
A single quote inside single quotes |
\" |
A double quote inside double quotes |
\` |
A backtick inside backticks |
console.log('Priya\'s order');
console.log("Priya's order");
Priya's order
Priya's order
The second is better. Switching quote style beats escaping whenever you can.
Strings do not change
This surprises people who expect an array-like thing to behave like an array.
const item = 'dal';
item[0] = 'D';
console.log(item);
dal
Nothing happened. No error either — the assignment was simply ignored. Inside a module, where strict mode is always on, you do get told:
TypeError: Cannot assign to read only property '0' of string 'dal'
Every string method returns a new string rather than changing the original. So this does nothing useful:
let item = 'dal';
item.toUpperCase();
console.log(item);
dal
You have to keep the result:
item = item.toUpperCase();
console.log(item);
DAL
Forgetting to keep the return value is one of the commonest beginner mistakes, and it fails silently every time.
The methods worth knowing
Keep this table. It covers almost everything you will need.
| Method | Example on 'toor dal' |
Result |
|---|---|---|
.length |
'toor dal'.length |
8 |
.toUpperCase() |
'TOOR DAL' |
|
.toLowerCase() |
'toor dal' |
|
.trim() |
' 411014 '.trim() |
'411014' |
.slice(a, b) |
'toor dal'.slice(0, 4) |
'toor' |
.slice(-n) |
'toor dal'.slice(-3) |
'dal' |
.at(-1) |
'l' |
|
.includes(x) |
'Pune 411014'.includes('411') |
true |
.startsWith(x) |
'411014'.startsWith('41') |
true |
.endsWith(x) |
||
.indexOf(x) |
'Pune'.indexOf('x') |
-1 when absent |
.replace(a, b) |
'1-2-3'.replace('-', '/') |
'1/2-3' |
.replaceAll(a, b) |
'1-2-3'.replaceAll('-', '/') |
'1/2/3' |
.split(sep) |
'dal,sugar'.split(',') |
['dal', 'sugar'] |
.repeat(n) |
'ab'.repeat(3) |
'ababab' |
.padStart(n, ch) |
String(7).padStart(2, '0') |
'07' |
.padEnd(n, ch) |
Three of those deserve more than a row.
.slice() takes negative indices and .substring() does not. You will meet
both; prefer slice.
console.log('toor dal'.slice(-3));
console.log('toor dal'.substring(-3));
dal
toor dal
substring treats the -3 as 0 and hands back the whole string — not an
error, just quietly wrong.
.indexOf() returns -1 when the thing is absent, not null and not
false. Since -1 is truthy, if (name.indexOf('x')) is true when the
character is missing, which is exactly backwards. Use .includes() unless you
actually want the position.
.replace() replaces only the first match. This is the trap in this lesson:
const messy = '9876-543-210';
console.log(messy.replace('-', ''));
console.log(messy.replaceAll('-', ''));
9876543-210
9876543210
Stripping punctuation from a phone number with .replace() looks like it works —
because it removes a dash — and then fails on the second one. Use
.replaceAll().
Formatting things people read
.padStart() is how you get leading zeros:
console.log('INV-' + String(7).padStart(4, '0'));
INV-0007
And slice is enough to format a phone number:
const phone = '9876543210';
console.log(`${phone.slice(0, 5)} ${phone.slice(5)}`);
98765 43210
Module 6 handles rupee and date formatting properly, with the tools built for it.
Comparing strings
=== is exact and case-sensitive:
console.log('Priya' === 'priya');
console.log('Priya'.toLowerCase() === 'priya');
false
true
For comparing user input, lowercase both sides. Trim them too — a pasted pincode
very often arrives as ' 411014 ', and ' 411014 ' === '411014' is false.
Sorting has a sharper edge:
console.log(['sugar', 'Dal', 'atta'].sort());
[ 'Dal', 'atta', 'sugar' ]
Dal comes first because the default sort compares character codes, and every
capital letter sorts before every lowercase one. A list of customer names sorted
this way puts everyone who typed a capital first, which looks broken to the
person reading it. The fix:
console.log(['sugar', 'Dal', 'atta'].sort((a, b) => a.localeCompare(b)));
[ 'atta', 'Dal', 'sugar' ]
localeCompare sorts the way a human expects. Arrays and sort are module 4;
the point here is that string comparison is not as simple as it looks.
Check your work
`${plates} plates — ₹${plates * rate}` with 3 plates at ₹80 gives
3 plates — ₹240. Expressions inside ${...} are evaluated, not just variables.
item[0] = 'D' does nothing. Strings are immutable. In an ordinary script it
fails silently; inside a module it is TypeError: Cannot assign to read only property '0' of string 'dal'.
item.toUpperCase() on its own leaves item unchanged. Every string method
returns a new string. You must assign the result.
'1-2-3'.replace('-', '/') is '1/2-3'. Only the first match. replaceAll
gives '1/2/3'.
'toor dal'.slice(-3) is 'dal'; .substring(-3) is 'toor dal'.
substring clamps a negative to zero instead of counting from the end.
'Pune'.indexOf('x') is -1, which is truthy — so if (s.indexOf('x'))
runs when the character is missing. Use .includes().
' 411014 ' === '411014' is false. Trim form input before comparing.
['sugar', 'Dal', 'atta'].sort() gives [ 'Dal', 'atta', 'sugar' ], because
capitals sort before lowercase by character code. localeCompare gives
[ 'atta', 'Dal', 'sugar' ].
String(7).padStart(4, '0') is '0007'.
Trim and lowercase in either order gives the same answer here.
' Priya '.trim().toLowerCase() and ' Priya '.toLowerCase().trim() both
match 'priya', because neither operation affects what the other does. Order
matters only when one step can create work for the other.
maskPhone:
function maskPhone(phone) {
const stars = '*'.repeat(phone.length - 5);
return phone.slice(0, 3) + stars + phone.slice(-2);
}
'9876543210' gives '987*****10'. Deriving the star count from the length
keeps the masked string the same length as the original, so an eight-digit
number gives '123***78' rather than something misaligned.
Practice
- Write a template literal that prints a customer, a plate count and a total,
doing the multiplication inside the
${...}. - Write a three-line delivery note as a single multiline template literal.
- Print
Priya's orderthree ways: escaping inside single quotes, using double quotes, and using backticks. Decide which you find most readable. - Prove strings are immutable. Try
item[0] = 'D'in a.jsfile and watch nothing happen. Then call.toUpperCase()without assigning, and confirm the original is unchanged. Both fail silently, which is why they are worth doing once deliberately. - Take
'9876-543-210'and strip the dashes with.replace(). Note the remaining dash. Fix it with.replaceAll(). - Format
'9876543210'as98765 43210usingslice. - Generate invoice numbers
INV-0001toINV-0012in a loop withpadStart. - Compare
' Priya 'with'priya'and make them match, usingtrimandtoLowerCase. Then decide which order to apply them in and whether it matters. - Sort
['sugar', 'Dal', 'atta', 'Besan']with plain.sort()and then withlocaleCompare. Explain why the first one looks broken to a human. - Harder. Write
maskPhone(phone)that turns'9876543210'into'987*****10'— first three digits, then stars for the middle, then the last two. Usesliceandrepeat, and work the star count out fromphone.lengthrather than writing5, so it still behaves sensibly if the number is a different length.
Next: operators, conditions and truthiness — including why if (count) treats a
genuine zero as missing.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship