JavaScript and TypeScript course Β· Module 2: Functions, Arrays and Objects
Template Literals and String Methods
In this lesson8
Every hour the Jurassic Park lab produces reports: species, weight, zone, health status. Gluing them together with pluses quickly turns into a thicket of quotes, and sensor data arrives with extra spaces and random letter case. Template literals and string methods are the tools that put text in order: from inserting variables to searching and transforming strings.
Template Literals - Basics
Template literals are strings wrapped in backticks, the reversed apostrophes, instead of ordinary quotes. You insert a JavaScript expression into them with the ${...} syntax:
1const species = 'T-Rex';
2const weight = 8000;
3
4// The old way - concatenation
5const old = 'Species: ' + species + ', weight: ' + weight + ' kg';
6
7// Template literal - much more readable!
8const report = `Species: ${species}, weight: ${weight} kg`;
9console.log(report); // "Species: T-Rex, weight: 8000 kg"Both variables hold the same text, but in the template you can see the shape of the sentence at a glance. That is why I recommend template literals whenever the text contains even one variable.
Expressions Inside ${}
Inside ${} you can place any JavaScript expression, not just a variable. The conditional operator, arithmetic, method calls and even a nested template literal all work here:
1const dino = { name: 'Rex', health: 85 };
2
3console.log(`Status: ${dino.health > 50 ? 'stable' : 'critical'}`);
4// "Status: stable"
5
6console.log(`Health: ${dino.health}%`);
7// "Health: 85%"
8
9console.log(`Double dose: ${dino.health * 2} units`);
10// "Double dose: 170 units"
11
12console.log(`Name: ${dino.name.toUpperCase()}`);
13// "Name: REX"JavaScript first evaluates the expression, then turns the result into text and pastes it in place of ${}. The dino object stays untouched.
Multi-line Strings
Template literals keep multi-line formatting, so you do not need to insert \n:
1const dinoCard = `
2ββββββββββββββββββββββββββ
3β Species: T-Rex β
4β Zone: A β
5β Status: Active β
6ββββββββββββββββββββββββββ
7`;
8console.log(dinoCard);Every line break and every space inside the backticks ends up in the result, including the empty line at the start.
Tagged Templates
Tagged templates let you process a template with a function. The "tag" function receives an array of the fixed text fragments and the values of the expressions, and there is always one more fragment than there are values:
1function highlight(strings, ...values) {
2 let result = '';
3 strings.forEach((str, i) => {
4 result += str;
5 if (i < values.length) {
6 result += '**' + values[i] + '**';
7 }
8 });
9 return result;
10}
11
12const name = 'Rex';
13const zone = 'A';
14const tagged = highlight`Dinosaur ${name} in zone ${zone}`;
15console.log(tagged);
16// "Dinosaur **Rex** in zone **A**"Notice that the tag is called without parentheses: the function name sits right before the backtick. A practical use is building database queries in which the values travel separately from the query text:
1function sql(strings, ...values) {
2 const query = strings.join('?');
3 return { query, params: values };
4}
5
6const zoneId = 'A';
7const minHealth = 80;
8const query = sql`SELECT * FROM dinos WHERE zone = ${zoneId} AND health > ${minHealth}`;
9// { query: "SELECT * FROM dinos WHERE zone = ? AND health > ?", params: ['A', 80] }The values were not pasted into the query text; they were set aside in params. Parameterized queries work on this principle, and they protect the database from SQL injection attacks.
String Methods - Searching
Let us start with searching for fragments in a DNA sequence. Each of these methods only reads the text and returns an answer:
1const dnaSequence = 'ATCG-TTGA-ACGT-TTGA';
2
3// indexOf() - returns the index of the first occurrence (or -1)
4console.log(dnaSequence.indexOf('TTGA')); // 5
5console.log(dnaSequence.indexOf('XXXX')); // -1
6console.log(dnaSequence.lastIndexOf('TTGA')); // 15 - the last occurrence
7
8// includes() - checks whether the string contains a fragment (boolean)
9console.log(dnaSequence.includes('ACGT')); // true
10console.log(dnaSequence.includes('ZZZZ')); // false
11
12// startsWith() / endsWith() - checks the start/end
13console.log(dnaSequence.startsWith('ATCG')); // true
14console.log(dnaSequence.endsWith('TTGA')); // true
15console.log(dnaSequence.startsWith('TTGA', 5)); // true - from position 5
16
17// match() with a regular expression - all matches
18console.log(dnaSequence.match(/TTGA/g)); // ['TTGA', 'TTGA']All of these methods are case-sensitive, so includes('acgt') returns false. When you only ask "is it there?", choose includes(), because it reads like a sentence, and keep indexOf() for when you need the position.
String Methods - Extracting and Splitting
Extracting comes in handy when you need only part of a specimen's full name:
1const specimen = 'Tyrannosaurus Rex';
2
3// slice(start, end) - extracts a fragment of the string
4console.log(specimen.slice(0, 13)); // "Tyrannosaurus"
5console.log(specimen.slice(14)); // "Rex"
6console.log(specimen.slice(-3)); // "Rex" (from the end)
7
8// split() - splits the string into an array
9const zones = 'A-B-C-D'.split('-');
10console.log(zones); // ['A', 'B', 'C', 'D']
11
12const words = 'Jurassic Park is great'.split(' ');
13console.log(words); // ['Jurassic', 'Park', 'is', 'great']slice does not include the character at index end, and a negative index counts from the end, so slice(-3) gives the last three characters. split returns an array, which you will unpack with destructuring in the next lesson.
String Methods - Transformations
Raw sensor data needs cleaning. The key rule: strings are immutable, so every method returns a new string:
1const dinoName = ' velociraptor ';
2
3// trim() - removes whitespace from the start and end
4console.log(dinoName.trim()); // "velociraptor"
5console.log(dinoName.trimStart()); // "velociraptor "
6console.log(dinoName.trimEnd()); // " velociraptor"
7
8// toUpperCase() / toLowerCase()
9console.log('rex'.toUpperCase()); // "REX"
10console.log('T-REX'.toLowerCase()); // "t-rex"
11
12// replace() - replaces the first occurrence
13const text = 'Zone A, Zone B, Zone A';
14console.log(text.replace('Zone', 'Sector')); // "Sector A, Zone B, Zone A"
15
16// replaceAll() - replaces all occurrences
17console.log(text.replaceAll('Zone', 'Sector')); // "Sector A, Sector B, Sector A"After these calls dinoName still has its spaces and text still contains "Zone". Store the result in a new variable if you want to keep it. So the difference between replace and replaceAll is the number of replacements, not whether the original changes.
String Methods - Padding
The padStart() and padEnd() methods pad a string to a given length, and repeat() copies it. Together they let you align a table of specimens:
1// padStart(length, fillString) - pads from the start
2const dinoId = '42';
3console.log(dinoId.padStart(5, '0')); // "00042"
4console.log(dinoId.padStart(5, '-')); // "---42"
5
6// padEnd(length, fillString) - pads from the end
7const label = 'Rex';
8console.log(label.padEnd(10, '.')); // "Rex......."
9
10// repeat(count) - repeats the string
11console.log('-'.repeat(30)); // "------------------------------"
12
13// A practical example - formatting a table
14const specimens = [
15 { id: '1', name: 'T-Rex' },
16 { id: '42', name: 'Velociraptor' },
17 { id: '100', name: 'Triceratops' }
18];
19
20specimens.forEach(s => {
21 const formattedId = s.id.padStart(4, '0');
22 const formattedName = s.name.padEnd(15, ' ');
23 console.log(`ID: ${formattedId} | Species: ${formattedName}`);
24});
25// ID: 0001 | Species: T-Rex
26// ID: 0042 | Species: Velociraptor
27// ID: 0100 | Species: TriceratopsThe first argument is the target length of the whole string, not the number of characters to add. That is why '42'.padStart(5, '0') adds three zeros.
Method Chaining
String methods can be chained, because each one returns a new string on which you call the next:
1const messy = ' jurassic PARK is GREAT ';
2const clean = messy.trim().toLowerCase().replace(/\s+/g, ' ');
3console.log(clean); // "jurassic park is great"
4
5const dinoCode = 'trex-001';
6const formatted = dinoCode.split('-')
7 .map(part => part.toUpperCase())
8 .join('-');
9console.log(formatted); // "TREX-001"/\s+/g is a regular expression meaning any run of whitespace characters, and the g flag replaces all occurrences. The same pattern creates friendly report addresses:
1function formatReport(rawInput) {
2 return rawInput
3 .trim()
4 .toLowerCase()
5 .replace(/\s+/g, '-') // spaces to hyphens
6 .replace(/[^a-z0-9-]/g, '') // remove anything other than letters, digits and hyphens
7 .slice(0, 50); // max 50 characters
8}
9
10console.log(formatReport(' T-Rex Escape!! Zone A '));
11// "t-rex-escape-zone-a"Each step of the chain does one thing, so it is easy to read and fix. I recommend breaking long chains the way it is done here, one method per line.
Practice
The sandbox contains report templates, DNA searching, slice, split, replace and padding. Change the arguments and predict the result before you run the code.
Code for this lesson: index.js
1// === TEMPLATE LITERALS AND STRING METHODS ===
2
3// 1. Template literals - inserting expressions
4const species = 'T-Rex';
5const weight = 8000;
6
7console.log('=== Template Literals ===');
8console.log(`Species: ${species}, weight: ${weight} kg`);
9console.log(`Health: ${weight > 5000 ? 'large specimen' : 'small specimen'}`);
10console.log(`Double dose: ${weight * 0.05} kg`);
11
12// 2. Multiline strings
13const card = `
14=== DINOSAUR CARD ===
15Species: ${species}
16Weight: ${weight} kg
17Status: active
18`;
19console.log(card);
20
21// 3. Search methods
22console.log('=== Searching ===');
23const dna = 'ATCG-TTGA-ACGT-TTGA';
24console.log('indexOf TTGA:', dna.indexOf('TTGA'));
25console.log('includes ACGT:', dna.includes('ACGT'));
26console.log('startsWith ATCG:', dna.startsWith('ATCG'));
27console.log('endsWith TTGA:', dna.endsWith('TTGA'));
28
29// 4. Slicing and splitting
30console.log('\n=== Slice and Split ===');
31const specimen = 'Tyrannosaurus Rex';
32console.log('slice(0,13):', specimen.slice(0, 13));
33console.log('slice(-3):', specimen.slice(-3));
34console.log('split:', 'A-B-C-D'.split('-'));
35
36// 5. Transformations
37console.log('\n=== Transformations ===');
38console.log('trim:', ' rex '.trim());
39console.log('toUpperCase:', 'rex'.toUpperCase());
40console.log('replace:', 'Zone A, Zone B'.replace('Zone', 'Area'));
41console.log('replaceAll:', 'Zone A, Zone B'.replaceAll('Zone', 'Area'));
42
43// 6. Padding
44console.log('\n=== Padding ===');
45console.log('padStart:', '42'.padStart(5, '0'));
46console.log('padEnd:', 'Rex'.padEnd(10, '.'));
47
48// 7. Method chaining
49console.log('\n=== Chaining ===');
50const messy = ' JURASSIC park ';
51const clean = messy.trim().toLowerCase();
52console.log('Result:', clean);Remember: a template literal is a label with blank fields for data, and string methods are the lab assistant's scalpels, which never cut the original sample but always make a copy.
Spotted a mistake in this lesson?
Check yourself
Answer the questions from this lesson. Pick an answer to see right away whether it is correct.
1. What character do we use to surround template literals in JavaScript?
2. What syntax allows you to insert a JavaScript expression inside a template literal?
3. What will 'Tyrannosaurus Rex'.slice(-3) return?
4. What is the difference between replace() and replaceAll() in JavaScript?
5. What will 'A-B-C-D'.split('-') return?
Hands-on tasks in the game
- Click in order
Arrange a call that checks whether dnaSequence contains the fragment 'ACGT':
- Horizontal ordering
Arrange the elements of calling padStart() to pad an ID with zeros: