RizTech Academy logo
RizTech Academy
Language BasicsLesson 3 of 520 min

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

  1. Write a template literal that prints a customer, a plate count and a total, doing the multiplication inside the ${...}.
  2. Write a three-line delivery note as a single multiline template literal.
  3. Print Priya's order three ways: escaping inside single quotes, using double quotes, and using backticks. Decide which you find most readable.
  4. Prove strings are immutable. Try item[0] = 'D' in a .js file 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.
  5. Take '9876-543-210' and strip the dashes with .replace(). Note the remaining dash. Fix it with .replaceAll().
  6. Format '9876543210' as 98765 43210 using slice.
  7. Generate invoice numbers INV-0001 to INV-0012 in a loop with padStart.
  8. Compare ' Priya ' with 'priya' and make them match, using trim and toLowerCase. Then decide which order to apply them in and whether it matters.
  9. Sort ['sugar', 'Dal', 'atta', 'Besan'] with plain .sort() and then with localeCompare. Explain why the first one looks broken to a human.
  10. Harder. Write maskPhone(phone) that turns '9876543210' into '987*****10' — first three digits, then stars for the middle, then the last two. Use slice and repeat, and work the star count out from phone.length rather than writing 5, 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