Mastering The Arcane Art Of JavaScript-mancy for C# Developers: A Guide to Strings, Finding The Right Words and Proper Spell Intonation
The Mastering the Arcane Art of JavaScript-mancy series are my humble attempt at bringing my love for JavaScript to all other C# developers that haven’t yet discovered how awesome this language and its whole ecosystem are. These articles are excerpts of the super duper awesome JavaScript-Mancy book a compendium of all things JavaScript for C# developers.
An Introduction To Strings In JavaScript
Strings are a primitive type in JavaScript that you can use to represent text literals much in the same way that you do in C#. In this article you’ll learn all you need to know about using strings in JavaScript and the exciting new string features that come with the new version of the language ECMAScript 6: template literals and tags.
Let’s start with the basics first!
The Basic Of Strings
You can experiment with all examples in this article directly within this jsBin example
As we introduced above and within the introduction of the book, string is one of the core primitive types in JavaScript. You create a string by either using single '
or double quotes "
.
// you can create a string using double quotes
> typeof "Klaatu... verata... n... Necktie. Nectar. Nickel. Noodle."
// => string
// or using single quotes
> typeof 'Klaatu... verata... n... Necktie. Nectar. Nickel. Noodle.'
// => string
You will often use a single quoted '
string to include "
double quotes inside the string and vice versa:
// you'll often use a ' to escape "
> "Ya ain't gonna need that fairy dust!"
// => ya ain't gonna need that fairy dust!
// and vice versa
> 'it was, in essence, a sophisticated heat beam which we called a "laser."'
// => it was, in essence, a sophisticated heat beam which we call a "laser"
You can concatenate two strings using the +
operator:
> "Three rings " + "for the elves"
// => three rings for the elves
Which is often used to inject values within a string and thus create text based on data:
> var conan = {toString: function() {return 'Conan, the cimmerian';}}
> conan + " was a famous hero of a past age"
// => Conan, the cimmerian was a famous hero of a past age
You can also create multiline strings by using the +
operator:
> "There are few men with more blood on their hands than me. " +
"None, that I know of. " +
"The Bloody-Nine they call me, my enemies, and there’s a lot of ’em"
// => "There are few men with more blood on their hands than me. None, that I know of.The Bloody-Nine they call me, my enemies, and there’s a lot of ’em"
or, alternatively, with a backslash at the end of each line \
:
> "There are few men with more blood on their hands than me.\
None, that I know of.\
The Bloody-Nine they call me, my enemies, and there’s a lot of ’em"
// => "There are few men with more blood on their hands than me. None, that I know of.The Bloody-Nine they call me, my enemies, and there’s a lot of ’em"
Additionally, you can insert new lines using the newline character ‘\n’:
> "There are few men with more blood on their hands than me.\n None, that I know of.\n The Bloody-Nine they call me, my enemies, and there’s a lot of ’em"
// => "There are few men with more blood on their hands than me.
// None, that I know of.
// The Bloody-Nine they call me, my enemies, and there’s a lot of ’em"
And as you can imagine, JavaScript uses the backslash `\’ as escape character:
> "\""
// => "
> '\''
// => '
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 strings.
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 but they lack most of the methods of an array, and moreover are immutable.
Just like in C#, strings are immutable and performing operations on a string doesn’t change the current string but creates a new one.
var justDoIt = 'Just DO IT!'
// they have a length property
console.log('length of justDoIt: ' + justDoIt.length)
// => length of justDoIt: 11
// they can be enumerated
for (var c in justDoIt) console.log(justDoIt[c])
// => J
// => u
// => etc
// they don't have array methods
console.log(justDoIt.forEach)
// => undefined
Even though they are not arrays you can use some of the array functions on a string. For instance, let’s try to traverse a string with forEach
:
> Array.prototype.forEach.call(justDoIt, function(c){console.log(c);})
// => J
// => u
// => etc...
or inject an arbitrary string between each character of the array with join
:
Array.prototype.join.call(justDoIt, '--')
// => J--u--s--t-- --D--O-- --I--T--!
But if we try to use reverse
the method throws an error:
Array.prototype.reverse.call(justDoIt)
// BOOM!
// TypeError: cannot assign to read only property '0'....
The error message gives us some hints as to why reverse
doesn’t work on strings: the implementation of reverse is trying to do a reverse in place and because a string is immutable it doesn’t work. So remember, you can use array methods on strings as long as they don’t attempt to mutate the original array.
Performing Operations with strings
In addition to being able to use these array methods on strings, the string type itself provides a series of methods that help you perform the most common operations.
You can concatenate strings with concat
in much the same way as you would use the +
operator:
> String.prototype.concat('hello my nemesis', 'we meet again')
// => hello my nemesis we meet again
> justDoIt.concat(' - Shia Labeaouf')
// => Just DO IT! - Shia Labeaouf
You can extract a character with chartAt
:
> justDoIt.charAt(0)
// => j
You can find whether or not a given text exists within a string by using indexOf
. The indexOf
method returns the position of the first occurrence of a given substring but you’ll often see it used just to find whether or not a substring exists within a string:
> justDoIt.indexOf('DO') !== -1
// => true
> justDoIt.indexOf('DO')
// => 5
Alternatively you can use the search
method that expects a regular expression as argument and behaves just like indexOf
:
> justDoIt.search(/DO/)
// => 5
Or the match
method that lets you find matches within the string according to a regular expression that you pass yourself as argument:
> justDoIt.match(/DO/)
// => ["DO"]
> justDoIt.match(/DO.*/)
// => ["DO IT!"]
The replace
method lets you replace a piece of text with another of your choice:
> justDoIt.replace('DO IT!', 'DANCE!')
// => Just DANCE!
Since the replace
method takes a regular expression as argument, you can match all occurrences of a substring within a string and replace them at once (by using the g
flag):
> 'a dragon is a creature that can breathe fire'.replace(/a /g, 'the ')
// => the dragon is the creature that can breathe fire
The substr
and substring
methods let you extract substrings 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:
> 'a dragon is a creature that can breathe fire'.substr(2, 6)
// => dragon
> 'a dragon is a creature that can breathe fire'.substring(2, 6)
// => drag
You can split a string in several pieces using the split
method. The resulting pieces of a split are stored inside an array and return:
> 'a dragon is a creature that can breathe fire'.split(' ');
// => ["a", "dragon", "is", "a", "creature", "that", "can", "breathe", "fire"]
The split
and join
methods make converting a string into an array and vice versa trivial. Using any of those methods allow you to take advantage of either the string methods or the array methods with no limitations.
> 'a dragon is a creature that can breathe fire'.split(' ').join(' ');
// => 'a dragon is a creature that can breathe fire'
New String Features in ES6
ES6 brings a lot of exciting new features to strings. First off, it comes with a whole new series of very helpful string methods like startsWith
and endsWith
. Secondly and even more important it comes with a complete overhaul of how we define strings in JavaScript with the addition of ES6 Template Literals (also known as String Templates). These 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 very common methods you often use in C# Contains
, StartsWith
and EndsWith
. Well ES6 brings all these new methods to JavaScript strings:
startsWith
and endsWith
work just like in C#:
> 'thunder and lightning!'.startsWith('thunder')
// => true
> 'thunder and lightning!'.endsWith('lightning!')
// => true
and includes
performs the same function than C# Contains
method, by checking whether or not a piece of text is contained within a string:
> 'thunder and lightning!'.includes('thunder')
// => true
> 'thunder and lightning!'.includes('lightning!')
// => true
> 'thunder and lightning!'.includes('and')
// => true
Finally, ES6 brings the repeat
method that allows you to create a new string from repeating an existing string:
> 'NaN'.repeat(10) + ' BatMan!'
// => NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN BatMan!
The Awesomeness of ES6 Template Literals
ES6 template literals provide a new and very powerful way of working with strings in JavaScript. You can create a string using a template literal by wrapping some text between backticks:
> `Rain fire and destruction upon thy enemies!`
// => Rain fire an destruction upon thy enemies!
You can also include both single and double quotes within a template literal freely without needing to escape them:
> `Rain fire n' destruction upon thy "enemies"!`
//=> 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 know as string interpolation:
let target = 'Sauron',
spell = 'balefire'
console.log(`Blast ${target} with ${spell}!`)
// => blast Sauron with balefire!
// prior to ES6 we would've needed to write:
// '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, the expressions within ${}
are not limited to variables but any valid expression in JavaScript. For instance:
function calculateDamage(modifier) {
return Math.round(modifier * Math.random())
}
console.log(
`Blast ${target} with ${spell} making ${calculateDamage(10)} damage`
)
// => Blast Sauron with balefire making 4 damage
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:
let multiline = `I start in this line,
and then I go to the next,
because there are few things in life,
that I like best`
console.log(multiline)
// => I start on this line,
// and then I go to the next...
Tags
Tags are a very interesting feature of template literals that 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 like this:
let clothes = 'boots',
orcLikesBoots = orcSpeech`I like those ${clothes} that you're wearing!`
A tag itself is merely a function that takes each string literal of a template and each substitution (the ${}
tokens) and returns the parsed string after composing literals and substitution together. The orcSpeech
tag could look like this:
function orcSpeech(literals, ...substitutions) {
console.log(literals) // => ['I like those ', ' that you're wearing']
console.log(substitutions) // => ['boots']
let phrase = literals[0]
substitutions.forEach(function(s, idx) {
phrase += `${s}${literals[idx + 1]}`
})
return phrase.replace(/s/g, 'sss').replace(/r/g, 'rr')
}
In the example above we compose literals and substitutions and replace s
and r
with sss
and rr
respectively to give an arbitrary sentence a more orcish feeling.
console.log(orcLikesBoots)
// => 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:
let orcLikesBoots = orcSpeech`I like those ${clothes} that you're wearing!`
as calling orcSpeech
as a function:
let orcLikesBoots = 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:
function orcSpeechRaw(literals, ...substitutions) {
console.log(literals.raw) // => ['I like those ', ' that you're wearing']
console.log(substitutions) // => ['boots']
let phrase = literals.raw[0]
substitutions.forEach(function(s, idx) {
phrase += `${s}${literals.raw[idx + 1]}`
})
return phrase.replace(/s/g, 'sss').replace(/r/g, 'rr')
}
In this example above you can appreciate how the literals
array exposes a raw
property that contains the same information than the literals
array but in raw format. If you compare the output of the non-raw tag orcSpeech
:
console.log(orcSpeech`I like those ${clothes}\n\n that you're \twearing!`)
// => ["I like those ", "
//
// that you're wearing!"]
// ['boots']
// I like thossse bootsss
//
// that you'rre wearring
Where the special characters have been transformed into whitespace, with the output of the raw tag orcSpeechRaw
:
console.log(orcSpeechRaw`I like those ${clothes}\n\n that you're \twearing!`)
// => ["I like those ", "\n\n that you're \twearing!"]
// ["boots"]
// "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 formatting functions that you can reuse later on, or even small text manipulation DSL’s (Domain Specific Languages) for your web application. For instance, an interesting application of tagged template literals could be building your own HTML templating engine:
let inventory = [
{ name: 'rusty sword', price: 2 },
{ name: 'health potion', price: 10 },
{ name: 'medallion of Valor', price: 300 },
]
console.log(ul`
${inventory.map(function(item) {
return li`${item.name}: ${item.price} silvers`
})}
`)
// => "<ul>
// <li>rusty sword: 2 silvers</li>
// <li>health potion: 10 silvers</li>
// <li>medallion of Valor: 300 silvers</li>
// </ul>"
Where we would create a ul
and li
tags that would be an extension over the more generic html
tag:
function html(literals, ...substitutions) {
let phrase = literals[0]
substitutions.forEach(function(s, idx) {
if (Array.isArray(s)) s = s.join('\n') // if array convert ot string
phrase += `${s}${literals[idx + 1]}`
// you could also add some special characters processing
// for parsing non HTML compliant characters like &
})
return phrase
}
function ul(literals, ...substitutions) {
return `<ul>${html(literals, ...substitutions)}</ul>`
}
function li(literals, ...substitutions) {
return `<li>${html(literals, ...substitutions)}</li>`
}
The code when applying the templating engine could be even simplified if we used ES6 arrow functions.
ul`${inventory.map(item => li`${item.name}: ${item.price} silvers`)}`)
Beautiful!
Concluding
And we got to the end! In this article 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 string 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 comes with new string methods like startsWith
and endsWith
but more importantly with a new way to work with strings in the form of template literals. Template literals are a great improvement because they let you inject values within strings in a very straightforward fashion and make creating multiline strings seamless. Tags are a special flavor of 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.
Interested in Learning More JavaScript? Buy the Book!
Are you a C# Developer interested in learning JavaScript? Then take a look at the JavaScript-mancy book, a complete compendium of JavaScript for C# developers.
Have a great week ahead!! :)
More Articles In This Series
- Chapter 0: The Basic Ingredients of JavaScriptMancy
- Chapter 1: The Many a One JavaScript Quirks
- Functions
- Object Oriented Programming
- Basics
- ES6
- OOP
- Introduction to OOP in JavaScript for C# Developers
- Summoning Fundamentals: Introduction to OOP In JavaScript – Encapsulation
- Summoning Fundamentals: Introduction to OOP In JavaScript – Inheritance
- Summoning Fundamentals: Introduction to OOP In JavaScript – Polymorphism
- White Tower Summoning: Mimicking C# Classical Inheritance in Javascript
- White Tower Summoning Enhanced: The Marvels of ES6 Classes
- Black Tower Summoning: Object Composition with Mixins
- Safer JavaScript Composition With Traits and Traits.js
- JavaScript Ultra Flexible Object Oriented Programming with Stamps
- Object Oriented JavaScript for C# Programmers
- Tooling
- Data Structures
- Functional Programming
- And more stuff to be shaped out:
- Functional Programming with JavaScript
- Modules
- Asynchronous Programming
- The JavaScript Ecosystem
Written by Jaime González García , dad, husband, software engineer, ux designer, amateur pixel artist, tinkerer and master of the arcane arts. You can also find him on Twitter jabbering about random stuff.