A Guide to Strings, Finding the Right Words and Proper Spell Intonation
Klaatu...
verata...
n...
Necktie.
Nectar. Nickel. Noodle.
- Ash
Epic Hero of Ages
Find the Right Words You Must
The use of words, text and text manipulation is commonplace in applications today. Often times applications will provide some sort of user interface as a way to allow user interaction. This UI will contain a myriad of textual information in various forms like labels, tooltips, help texts, text-based content, etc. Even applications that don’t expose a user interface will often log information to the filesystem or to analytics services for troubleshooting and monitoring.
JavaScript, like many other languages, has a primitive type that catters to all your text representation and manipulation needs, the string. In this chapter you’ll learn all you need to know about strings in JavaScript and the exciting new features that come with ES6: template literals and tags.
Let’s start with the basics first!
The Basic Of Strings
You create a string by either using single ' or double quotes ".
1 // you can create a string using double quotes
2 > typeof "Klaatu... verata... n... Necktie. Nectar. Nickel. Noodle."
3 // => string
4
5 // or using single quotes
6 > typeof 'Klaatu... verata... n... Necktie. Nectar. Nickel. Noodle.'
7 // => string
You will often use a single quoted ' string to include " double quotes inside the string and vice versa:
1 // you'll often use a ' to escape "
2 > "Ya ain't gonna need that fairy dust!"
3 // => ya ain't gonna need that fairy dust!
4
5 // and vice versa
6 > 'it was, in essence, a sophisticated heat beam which we called a "\
7 laser".'
8 // => it was, in essence, a sophisticated heat
9 // beam which we call a "laser".
You can concatenate two strings using the + operator:
1 > "Three rings " + "for the elves"
2 // => three rings for the elves
The + operator is often used to inject values within a string and thus create text based on data:
1 > var conan = {toString: function() {return 'Conan, the cimmerian';}}
2 > conan + " was a famous hero of a past age"
3 // => Conan, the cimmerian was a famous hero of a past age
You can also create multiline strings using the same operator:
1 > "There are few men with more blood on their hands than me. " +
2 "None, that I know of. " +
3 "The Bloody-Nine they call me, my enemies, and there’s a lot of ’em"
4 // => "There are few men with more blood on their hands than me.
5 // None, that I know of. The Bloody-Nine they call me, my
6 // enemies, and there’s a lot of ’em"
Or, alternatively, with a backslash at the end of each line \:
1 > "There are few men with more blood on their hands than me.\
2 None, that I know of.\
3 The Bloody-Nine they call me, my enemies, and there’s a lot of ’em"
4 // => "There are few men with more blood on their hands than me.
5 // None, that I know of. The Bloody-Nine they call me, my
6 // enemies, and there’s a lot of ’em"
Additionally, you can insert new lines using the newline character ‘\n’:
1 > "There are few men with more blood on their hands than me.\n None,\
2 that I know of.\n The Bloody-Nine they call me, my enemies, and the\
3 re’s a lot of ’em"
4 // => "There are few men with more blood on their hands than me.
5 // None, that I know of.
6 // The Bloody-Nine they call me, my enemies, and there’s a
7 // lot of ’em"
As you may have deduced from the previous example, JavaScript uses the backslash `\’ as escape character:
1 > "\""
2 // => "
3 > '\''
4 // => '
Now that you’ve got a better grasp of strings in JavaScript, let’s take a look at the different operations you can perform on them.
Are Strings Arrays of Characters?
JavaScript strings are not arrays of characters, they behave more like array-like objects. They have a length property, they can be enumerated and indexed but they lack most of the methods of an array. They are also immutable.
1 var justDoIt = 'Just DO IT!';
2
3 // they have a length property
4 console.log('length of justDoIt: ' + justDoIt.length);
5 // => length of justDoIt: 11
6
7 // they can be enumerated
8 for (var c in justDoIt) console.log(justDoIt[c]);
9 // => J
10 // => u
11 // => etc
12
13 // they don't have array methods
14 console.log(justDoIt.forEach)
15 // => undefined
Even though strings are not arrays, you can use some of the array functions on a string by borrowing them from Array.prototype. For instance, you can traverse each character in a string using forEach:
1 > Array.prototype.forEach.call(justDoIt,
2 function(c){console.log(c);})
3 // => J
4 // => u
5 // => etc...
Or inject an arbitrary string between each character with join:
1 Array.prototype.join.call(justDoIt, '--')
2 // => J--u--s--t-- --D--O-- --I--T--!
However, if we try to use reverse it throws an error:
1 Array.prototype.reverse.call(justDoIt);
2 // BOOM!
3 // TypeError: cannot assign to read only property '0'....
The error message gives us some hints as to why reverse doesn’t work: The implementation of reverse is trying to do an in-place string reversal. Because strings are immutable, attempting to replace the first character in a string with the last one causes an error and therefore the "cannot assign to read only property '0'".
Remember, you can use any array methods on strings as long as they don’t attempt to mutate the original string.
Performing Operations with strings
In addition to these array methods, the string type also provides its own series of methods to help you perform the most common text operations.
You can concatenate strings with concat just like you did with the + operator:
1 > String.prototype.concat('hello my nemesis', 'we meet again')
2 // => hello my nemesis we meet again
3
4 > justDoIt.concat(' - Shia Labeaouf')
5 // => Just DO IT! - Shia Labeaouf
You can obtain an uppercase or lowercase version of an existing string using toUpperCase and toLowerCase:
1 console.log(justDoIt.toUpperCase());
2 // => JUST DO IT!
3
4 console.log(justDoIt.toLowerCase());
5 // => just do it!
You can extract a character at a specific position with chartAt:
1 > justDoIt.charAt(0)
2 // => j
The indexOf method returns the position of the first occurrence of a piece of text within a string.
1 > justDoIt.indexOf('DO')
2 // => 5
You’ll often see it used to find out whether a piece of text exists in a string by comparing it to -1:
1 // indexOf returns `-1` when it can't find the piece of text
2 > justDoIt.indexOf('DO') !== -1
3 // => true
4 > justDoIt.indexOf('Sand castle') !== -1
5 // => false
Alternatively you can use the search method. It is an enhanced version of indexOf that allows you to specify what you are looking for using a regular expression:
1 > justDoIt.search(/DO/)
2 // => 5
match is, in turn, an enhanced version of search that lets you find multiple matches within a string according to a regular expression of your choice:
1 > justDoIt.match(/DO/)
2 // => ["DO"]
3
4 > justDoIt.match(/DO.*/)
5 // => ["DO IT!"]
The replace method lets you replace a piece of text with another one of your own choosing:
1 > justDoIt.replace('DO IT!', 'DANCE!')
2 // => Just DANCE!
Since replace also allows for regular expressions, you can match all occurrences of a substring and replace them at once. Just use the g flag (which stands for global):
1 > 'a dragon is a creature that can breathe fire'
2 .replace(/a /g, 'the ')
3 // => the dragon is the creature that can breathe fire
The substr and substring methods let you extract bits of text from an array by specifying indexes. The former expects the start index and the length of the substring whilst the latter expects the starting and ending indexes:
1 // String.prototype.substr(startIndex, length)
2 > 'a dragon is a creature that can breathe fire'.substr(2, 6)
3 // => dragon
4
5 // String.prototype.substring(startIndex, endIndex)
6 > 'a dragon is a creature that can breathe fire'.substring(2, 6)
7 // => drag
You can split a string in several pieces using the split method. The resulting pieces of splitting the string are returned as items of an array:
1 > 'a dragon is a creature that can breathe fire'.split(' ');
2 // => ["a", "dragon", "is", "a", "creature", "that",
3 // "can", "breathe", "fire"]
The split and join methods make it dead easy to convert a string into an array and vice versa. Using them will allow you to take advantage of both the string and array methods without limitations. You have a string and want to use the Array.prototype.map method? Convert it into an array via split, perform whichever operations you need and then use join to get your string back.
1 > 'a dragon is a creature that can breathe fire'.split(' ')
2 .join(' ');
3 // => 'a dragon is a creature that can breathe fire'
New String Features in ES6
ES6 brings a lot of exciting new features to strings:
- Several new helpful methods like
startsWithandendsWith. - A complete overhaul of how we define strings in JavaScript with Template Literals20. Template Literals provide a much better support for string interpolation, multiline strings, HTML-friendly strings and the ability to create reusable string formatters called tags.
ES6 Brings Some New String Methods
After reading the previous sections you may have missed three methods you often use in C#: Contains, StartsWith and EndsWith. Well, worry no more because ES6 brings all these new methods to JavaScript strings:
The startsWith and endsWith methods work just like in C#. The first one verifies whether a string starts with a given substring and the latter checks whether or not a string ends with a given piece of text:
1 > 'thunder and lightning!'.startsWith('thunder')
2 // => true
3 > 'thunder and lightning!'.endsWith('lightning!')
4 // => true
The includes method performs the same function as C# Contains by checking whether or not a piece of text is contained within a string:
1 > 'thunder and lightning!'.includes('thunder')
2 // => true
3 > 'thunder and lightning!'.includes('lightning!')
4 // => true
5 > 'thunder and lightning!'.includes('and')
6 // => true
Note how using the includes method provides a much better developer experience and readable code than the indexOf method from previous sections.
Finally, ES6 brings the repeat method that allows you to create a new string by repeating an existing string a specific number of times:
1 > 'NaN'.repeat(10) + ' BatMan!'
2 // => NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN BatMan!
The Awesomeness of ES6 Template Literals
ES6 template literals provide a new and very powerful way of working with strings. You can create a string using a template literal by wrapping some text between backticks:
1 > `Rain fire and destruction upon thy enemies!`
2 // => Rain fire an destruction upon thy enemies!
Template literals let you use both single and double quotes freely without the need to escape them:
1 > `Rain fire n' destruction upon thy "enemies"!`
2 //=> Rain fire n' destruction upon thy "enemies"!
One of the greatest strengths of template literals is that you can inject values in a very straightforward and readable fashion. By using the notation ${target} inside the template literal you can include the value of the variable target in the resulting string. This is also known as string interpolation:
1 let target = 'Sauron', spell = 'balefire'
2 console.log(`Blast ${target} with ${spell}!`)
3 // => blast Sauron with balefire!
4
5 // prior to ES6 we would've needed to write:
6 // 'blast' + target + 'with' + spell
You can include any variable that is accessible within the scope where the template literal is declared. Say goodbye to concatenating strings and variables with the + operator. Bye and farewell!
Additionally, you are not limited to using variables when doing string interpolation. You can use any valid JavaScript expression. For instance, you could call a function:
1 function calculateDamage(modifier){
2 return Math.round(modifier*Math.random())
3 }
4 console.log(`Blast ${target} with ${spell} making
5 ${calculateDamage(10)} damage`)
6 // => Blast Sauron with balefire making 4 damage
Or perform arithmetics:
1 console.log(`1 + 1 is not ${1+1} ===> SYNERGY!!!`);
2 // => 1 + 1 is not 2 ===> SYNERGY!!!
Another great improvement from template literals over vanilla strings are multiline strings. With template literals, if you want to have a multiline string, you just write a multiline string. It’s that easy:
1 let multiline = `I start in this line,
2 and then I go to the next,
3 because there are few things in life,
4 that I like best`
5 console.log(multiline);
6 // => I start on this line,
7 // and then I go to the next...
Tags
Tags are a very interesting feature of template literals. They allow you to customize how a specific template literal gets parsed into a string.
To apply a tag you prepend its name to the template literal:
1 // appreciate how the orcSpeech tag appears before
2 // the string template
3 let clothes = 'boots';
4 let orcLikesBoots = orcSpeech`I like those ${clothes} that you're we\
5 aring!`
In this example we have created a tag orcSpeech to parse any piece of speech into the way orcs speak (who I’ve heard speak numerous times). When we evaluate the resulting string we can verify how the tag has transformed the original text into garbled orc speech:
1 console.log(orcLikesBoots);
2 // => I like thossse bootsss that you'rre wearring!
How did that happen? Well, a tag is merely a function and what you see above is the result of calling that function with the string literal as input.
More specifically, a tag function takes each string literal of a template and each substitution (the ${} tokens) and returns the parsed string after composing literals and substitution together:
1 function orcSpeech(literals, ...substitutions){
2 // do your magic
3 // return parsed string
4 }
In the previous example the literals and substitutions would be:
1 literals => "I like those", " that you're wearing"
2 substitutions => ${clothes}
The implementation of the orcSpeech tag function could look like this:
1 function orcSpeech(literals, ...substitutions){
2 console.log(literals); // => ['I like those ',
3 // ' that you're wearing']
4 console.log(substitutions); // => ['boots']
5
6 let phrase = literals[0];
7 substitutions.forEach(function(s, idx){
8 phrase += `${s}${literals[idx+1]}`
9 });
10
11 return phrase.replace(/s/g, 'sss').replace(/r/g, 'rr')
12 }
Where we compose literals and substitutions and then replace s and r with sss and rr respectively giving the original sentence that rough touch characteristic of orcs, goblins and other creatures of darkness.
1 console.log(orcLikesBoots);
2 // => I like thossse bootsss that you'rre wearring!
When the tag is applied to a template literal like you saw above, the effect is the same as that of calling the tag function with the literals and substitutions. So this down here would be the same:
1 let orcLikesBoots =
2 orcSpeech`I like those ${clothes} that you're wearing!`
As calling orcSpeech as a function:
1 let orcLikesBoots =
2 orcSpeech(['I like those ', " that you're wearing!"], 'boots');
In addition to what you’ve seen thus far, tags also give you the possibility to access the raw string literals. Having access to raw literals lets you customize even how you parse special characters such as end of line /n, tabs /t, etc.
We can illustrate this with an example of a hypothetical orcSpeechRaw tag:
1 function orcSpeechRaw(literals, ...substitutions){
2 console.log(literals.raw); // => ['I like those ',
3 // ' that you're wearing']
4 console.log(substitutions); // => ['boots']
5
6 let phrase = literals.raw[0];
7 substitutions.forEach(function(s, idx){
8 phrase += `${s}${literals.raw[idx+1]}`
9 });
10
11 return phrase.replace(/s/g, 'sss').replace(/r/g, 'rr')
12 }
The literals array exposes a raw property that contains the same information than the literals array but in raw format. If you take a look at the output of the non-raw tag orcSpeech:
1 console.log(
2 orcSpeech`I like those ${clothes}\n\n that you're \twearing!`)
3 // => ["I like those ", "
4 //
5 // that you're wearing!"]
6 // ['boots']
7 // I like thossse bootsss
8 //
9 // that you'rre wearring
You’ll be able to appreciate how the special characters have been transformed into whitespace. If you then take a look at the output of the raw tag orcSpeechRaw:
1 console.log(
2 orcSpeechRaw`I like those ${clothes}\n\n that you're \twearing!`)
3
4 // => ["I like those ", "\n\n that you're \twearing!"]
5 // ["boots"]
6 // "I like thossse bootsss\n\n that you'rre \twearring!"
You’ll see how the special characters are available in the literals.raw array.
In summary, tags give you the opportunity of creating reusable formatting functions or even small text manipulation DSL’s (Domain Specific Languages) for your web applications. For instance, an interesting application of tagged template literals could be building your own HTML templating engine:
1 let inventory = [
2 {name: 'rusty sword', price: 2},
3 {name: 'health potion', price: 10},
4 {name: 'medallion of Valor', price: 300}
5 ];
6
7 console.log(ul`
8 ${inventory.map(function(item){
9 return li`${item.name}: ${item.price} silvers`
10 })}`)
11 // => "<ul>
12 // <li>rusty sword: 2 silvers</li>
13 // <li>health potion: 10 silvers</li>
14 // <li>medallion of Valor: 300 silvers</li>
15 // </ul>"
Where we would create a ul and li tags that would be an extension over the more generic html tag:
1 function html(literals, ...substitutions){
2 let phrase = literals[0];
3 substitutions.forEach(function(s, idx){
4
5 // if array convert to string
6 if (Array.isArray(s)) s = s.join('\n');
7
8 phrase += `${s}${literals[idx+1]}`;
9 // you could also add some special characters processing
10 // for parsing non HTML compliant characters like &
11 });
12 return phrase;
13 }
14
15 function ul(literals, ...substitutions){
16 return `<ul>${html(literals, ...substitutions)}</ul>`;
17 }
18
19 function li(literals, ...substitutions){
20 return `<li>${html(literals, ...substitutions)}</li>`;
21 }
The code when applying the templating engine could be simplified further if we used ES6 arrow functions which we’ll cover in the next chapter. But there’s nothing preventing us from taking a sneak-peek right?
1 ul`${inventory.map(item => li`${item.name}:${item.price} coins`)}`)
Beautiful!
String Cheatsheet
Basics
| Basics | description |
|---|---|
'a string', "a string"
|
Create a string |
"Y'all", 'the "laser"` |
Escape single and double quotes |
"You are: " + status |
Concatenate strings and values using +
|
"lalalala too long" + |
Concatenate for multiline strings |
"lala" |
|
"lalalala too long\ |
Create multiline strings using \ at the end of each row |
lala" |
|
"this is\n a new line" |
Special characters start with \
|
String Methods
| String Methods | description |
|---|---|
concat(str1, str2, ...) |
Concatenate strings |
- "this is".concat(" excellent")
|
- "this is excellent"
|
toUpperCase() |
Uppercase all characters in a string |
- "abracadabra".toUpperCase()
|
- "ABRACADABRA"
|
toLowerCase() |
Lowercase all characters in a string |
- "SHAZAM!".toLowerCase()
|
- "shazam"
|
charAt(position) |
Get character at given position |
- "Just DO IT!".charAt(0)
|
- "J"
|
indexOf(string) |
Gets position of the first occurrence of a string |
- "JUST DO IT!".indexOf("DO")
|
- 5
|
search(regExp) |
Gets position of the first occurrence of a regular expression |
- "JUST DO IT!".search(/DO/)
|
|
match(regExp) |
Find strings matching a regex |
- "JUST DO IT!".search(/DO.*/)
|
- ['DO', 'DO IT']
|
| `replace(str or regex, str) | Find and replace using a string or regex |
- "JUST DO IT!".replace('DO IT!', 'DANCE!')
|
- "JUST DANCE!"
|
split(separator) |
Separate string into an array of items using a separator |
- "JUST DO IT".split(" ")
|
- ["JUST", "DO", "IT"]
|
ES6 String Methods
| String Methods | description |
|---|---|
startsWith(str) |
Check whether a string starts with some text |
- "JUST DO IT".startsWith("JUST")
|
|
endsWith(str) |
Check whether a string ends with some text |
- "JUST DO IT".endsWith("IT")
|
|
includes(str) |
Check whether a string contains some text |
- "JUST DO IT".includes("DO")
|
|
repeat(times) |
Create a new string by repeating the current string N times |
- "Na".repeat(3)
|
- "NaNaNa"
|
ES6 Template Literals
| Template Literals | description |
|---|---|
`new literal` |
Create template literal |
- "new literal"
|
|
`I have ${coins} coins` |
Variable interpolation |
- "I have 10 coints"
|
|
`I have ${1+1} coins` |
Evaluate expressions |
- "I have 2 coins"
|
|
li`purse` |
Use of tags |
- "<li>purse</li>"
|
Concluding
And we got to the end! In this chapter you learned a ton about strings in JavaScript, you saw how strings are a primitive type which behave in a very similar way to C# strings. They are immutable and performing operations on them or changing them in some fashion results in a new string being created.
Strings behave like array-like objects and although they don’t have array methods, you can apply the non-destructive array methods on them by virtue of using call and apply. In addition to this, strings have a lot of methods that you can use to perform operations like concatenation, matching, searching, extracting and replacing pieces of text.
ES6 brings new string methods like startsWith and endsWith but more importantly it comes with a new way to work with strings in the form of template literals. Template literals are a great improvement over vanilla strings because they let you inject values within strings in a very straightforward fashion and make creating multiline strings seamless. Tags are a great companion to template literals that let you customize how a template literal is parsed into a string opening an endless world of possibilities when it comes to text manipulation in JavaScript.