The themes of today's translation of the JavaScript tutorial will be exception handling, automatic semicolon features, and pattern literals.
→
Part 1: first program, language features, standards→
Part 2: code style and program structure→
Part 3: Variables, Data Types, Expressions, Objects→
Part 4: Functions→
Part 5: Arrays and Loops→
Part 6: Exceptions, Semicolon, Pattern Literals→
Part 7: strict mode, this keyword, events, modules, mathematical calculations→
Part 8: ES6 feature overview→
Part 9: Overview of ES7, ES8, and ES9 Capabilities
Exception Handling
When a problem occurs during code execution, it is expressed as an exception in JavaScript. If you do not take measures to handle exceptions, then, if they occur, the program execution stops, and an error message is displayed in the console.
')
Consider the following code snippet.
let obj = {value: 'message text'} let notObj let fn = (a) => a.value console.log(fn(obj))
Here we have a function that we plan to use to process objects that have a
value
property. It returns this property. If you use this function according to its intended purpose, that is, you will not be given an object to work with which it is designed, when it is executed, no errors will be issued. But if you pass something inappropriate to it, in our case, a declared but uninitialized variable, then when you try to access the
value
property of the
value
undefined
an error will occur. The error message will get to the console, the program will stop running.
This is how it looks when running this code in the Node.js environment.
TypeError exception in Node.jsIf something similar is found in the JS code of a web page, a similar message will be sent to the browser console. If this happens in a real program, say - in the code of the web server, this behavior is highly undesirable. It would be good to have a mechanism that allows, without stopping the program, to catch the error, and then take steps to correct it. Such a mechanism exists in JavaScript; it is represented by the
try...catch
construction.
TryDesign try ... catch
The
try...catch
construct allows you to catch and handle exceptions. Namely, it includes a
try
block, which includes code that can cause an error, and a
catch
, to which control is passed when an error occurs.
try
blocks do not include absolutely all program code. There are those sections that can cause run-time errors. For example - calls to functions that have to work with some data obtained from external sources. If the structure of such data differs from that expected by the function, an error may occur. This is what the
try...catch
diagram looks like.
try {
If the code is executed without errors, the
catch
(exception handler) is not executed. If an error occurs, the object of the error is transmitted there and some actions are taken to combat this error.
Let us apply this construction in our example, protecting with it the dangerous sections of the program - those in which the function
fn()
called.
let obj = {value: 'message text'} let notObj let fn = (a) => a.value try { console.log(fn(obj)) } catch (e) { console.log(e.message) } console.log('Before')
Let's look at the results of executing this code in the Node.js environment.
Error handling in Node.jsAs you can see, if you compare this example with the previous one, now all the code is executed, and the one that is located before the problem line, and the one that is located after it. We “handle” the error by simply outputting to the console the values of the
message
property of an object of type
Error . What will be the handling of errors that have arisen in the actual code used, depends on the error.
Above, we discussed the
try...catch
, but, in fact, this construct includes another
finally
block.
Finally finally block
The
finally
block contains code that is executed regardless of whether or not an error has occurred in the code running in the
try
block. Here's what it looks like.
try {
The
finally
block can also be used if there is no
catch
block in the
try...catch...finally
block. With this approach, it is used in the same way as in the construction with the
catch
, for example, to release the resources occupied in the
try
block.
Try nested try blocks
Try blocks can be nested. An exception is processed in the nearest
catch
.
try {
In this case, if an exception occurs in an internal
try
block, it will be processed in an external
catch
.
▍ Self-generate exceptions
Exceptions can be generated independently using the
throw
statement. Here's what it looks like.
throw value
After this instruction is executed, control is transferred to the nearest
catch
, or if such a block cannot be found, the program is terminated. The exception value can be anything. For example, a user-defined error object.
About semicolons
Using semicolons in JavaScript code is optional. Some programmers do without them, relying on the automatic system of their arrangement, and putting them only where it is absolutely necessary. Some prefer to put them wherever possible. The author of this material refers to the category of programmers who seek to do without semicolons. He says that he decided to do without them in the fall of 2017, by setting up the Prettier so that it deletes them everywhere, where they can be dispensed with without their explicit insertion. In his opinion, the code without semicolons looks more natural and easier to read.
Perhaps we can say that the JS-developers community is divided into two camps with respect to a semicolon. At the same time, there are both JavaScript style guides, which prescribe explicit semicolon assignments, and guidelines that recommend doing without them.
All this is possible due to the fact that in JavaScript there is a system of automatic substitution of semicolons (Automatic Semicolon Insertion, ASI). However, the fact that in JS code, in many situations, you can do without these characters, and the fact that semicolons are automatically placed when preparing code for execution does not mean that the programmer does not need to know the rules by which this happens. Ignorance of these rules leads to errors.
▍ Rules for auto-substitution of semicolons
The JavaScript parser automatically adds semicolons when parsing program text in the following situations:
- When the next line starts with a code that interrupts the current code (the code of a certain command can be located on several lines).
- When the next line starts with a
}
, which closes the current block. - When the end of the file with the program code is detected.
- In the line with the command
return
. - In the line with the command
break
. - In the line with the
throw
command. - In the line with the command
continue
.
▍ Code examples that do not work as expected.
Here are some examples illustrating the above rules. For example, what do you think will be displayed as a result of executing the following code fragment?
const hey = 'hey' const you = 'hey' const heyYou = hey + ' ' + you ['h', 'e', 'y'].forEach((letter) => console.log(letter))
When trying to execute this code, an
Uncaught TypeError: Cannot read property 'forEach' of undefined
error will be
Uncaught TypeError: Cannot read property 'forEach' of undefined
system, based on rule No. 1, tries to interpret the code as follows.
const hey = 'hey'; const you = 'hey'; const heyYou = hey + ' ' + you['h', 'e', 'y'].forEach((letter) => console.log(letter))
The problem can be solved by putting a semicolon after the last but one line of the first example.
Here is another code snippet.
(1 + 2).toString()
The result of its execution will be the output line
"3"
. What happens if something like this appears in the following code snippet?
const a = 1 const b = 2 const c = a + b (a + b).toString()
In this situation, a
TypeError: b is not a function
error will appear
TypeError: b is not a function
since the above code will be interpreted as follows.
const a = 1 const b = 2 const c = a + b(a + b).toString()
Let us now look at an example based on rule No. 4.
(() => { return { color: 'white' } })()
You might think that this IIFE will return an object containing the
color
property, but in fact it is not. Instead, the function will return the value
undefined
as the system adds a semicolon after the
return
command.
In order to solve a similar problem, the opening curly bracket of an object literal must be placed on the same line as the
return
command.
(() => { return { color: 'white' } })()
If you look at the following code snippet, you might think that it will display
0
in the message box.
1 + 1 -1 + 1 === 0 ? alert(0) : alert(2)
But it prints 2, since, in accordance with rule No. 1, this code is represented as follows.
1 + 1 -1 + 1 === 0 ? alert(0) : alert(2)
Care should be taken when using semicolons in JavaScript. You can meet both hot supporters of semicolons and their opponents. In fact, deciding whether you need a semicolon in your code, you can rely on the fact that JS supports their automatic substitution, but everyone has to decide for himself whether they are needed in his code or not. The main thing is to consistently and reasonably apply the chosen approach. With regard to the semicolon arrangement and code structure, it is recommended to adhere to the following rules:
- Using the
return
command, place what it should return from the function, in the same line as this command. The same applies to the commands break
, throw
, continue
. - Pay special attention to situations where a new line of code begins with a parenthesis, since this line can be automatically combined with the previous one and presented by the system as an attempt to call a function or attempt to access an array element.
In general, we can say that, whether you put semicolons yourself, or rely on their automatic placement, test the code to make sure that it works exactly as expected.
Quotes and pattern literals
Let's talk about the use of quotes in JavaScript. Namely, we are talking about the following valid quote types in JS programs:
- Single quotes.
- Double quotes.
- Return quotes.
Single and double quotes, in general, can be considered the same.
const test = 'test' const bike = "bike"
There is practically no difference between them. Perhaps the only noticeable difference is that in the strings enclosed in single quotes, the single quotation symbol must be escaped, and in the strings enclosed in double quotation marks - the double one.
const test = 'test' const test = 'te\'st' const test = 'te"st' const test = "te\"st" const test = "te'st"
In various style guides, you can find a recommendation for using single quotes, as well as a recommendation for using double quotes. The author of this material says that in JS-code tends to use only single quotes, using double only in HTML-code.
Back quotes appeared in JavaScript with the release of the ES6 standard in 2015. They, besides other new features, allow conveniently describing multi-line strings. Such strings can also be specified using regular quotes - using the escape sequence
\n
. It looks like this.
const multilineString = 'A string\non multiple lines'
Back quotes (usually the button to enter them is to the left of the numeric key 1 on the keyboard) allow you to do without
\n
.
const multilineString = `A string on multiple lines`
But the possibilities of reverse quotes are not limited to this. So, if a string is described using backward quotes, it is possible to substitute values in it using the
${}
construct, which are the result of evaluating JS expressions.
const multilineString = `A string on ${1+1} lines`
Such strings are called template literals.
Pattern literals are distinguished by the following features:
- They support multiline text.
- They make it possible to interpolate strings, you can use embedded expressions in them.
- They allow you to work with tagged templates, making it possible to create your own domain-specific languages (DSL, Domain-Specific Language).
Let's talk about these opportunities.
▍Multiline text
When asking, using back quotes, multi-line texts, you need to remember that spaces in such texts are just as important as other characters. For example, consider the following multi-line text.
const string = `First Second`
His conclusion will give something like the following.
First Second
That is, it turns out that when this text was entered in the editor, it is possible that the programmer expected that the words
First
and
Second
, when outputted, would be strictly under each other, but in fact this is not the case. In order to circumvent this problem, you can begin multiline text with a line feed, and, immediately after the closing back quote, call the
trim()
method, which will remove the whitespace at the beginning or end of the line. These characters, in particular, include spaces and tabs. The end of line characters will also be removed.
It looks like this.
const string = ` First Second`.trim()
▍ Interpolation
Interpolation here refers to the conversion of variables and expressions to strings. This is done using the
${}
construct.
const variable = 'test' const string = `something ${ variable }`
You can add anything to the
${}
block, even expressions.
const string = `something ${1 + 2 + 3}` const string2 = `something ${foo() ? 'x' : 'y' }`
The text
something 6
will fall into the
string
constant, or the text
something x
, or the text
something y
will be written to the constant
string2
. It depends on whether the
foo()
function returns true or false (here we use the ternary operator, which, if what is before the question mark, is true, returns what comes after the question mark, otherwise returning what comes after the colon).
▍Tagged patterns
Tagged templates are used in many popular libraries. Among them -
Styled Components ,
Apollo ,
GraphQL .
The fact that such patterns are output is subject to some kind of logic defined by the function. Here is a slightly revised example given in one of our
publications , illustrating the work with tagged patterned strings.
const esth = 8 function helper(strs, ...keys) { const str1 = strs[0]
Here, if the number
8
written in the
esth
constant, the string
ES 8 is awesome
. Otherwise there will be another line. For example, if
esth
has the number
6
, then it will look like
ES 6 is good
.
Styled Components uses tagged templates to define CSS strings.
const Button = styled.button` font-size: 1.5em; background-color: black; color: white; `;
In Apollo, they are used to define GraphQL queries.
const query = gql` query { ... } `
Knowing how the tagged templates are arranged, it is easy to understand that
styled.button
and
gql
from the previous examples are just functions.
function gql(literals, ...expressions) { }
For example, the
gql()
function returns a string that can be the result of any calculation. The
literals
parameter of this function is an array containing the contents of the template literal
expresions
into parts,
expresions
contains the results of the evaluation of expressions.
Let's sort the next line.
const string = helper`something ${1 + 2 + 3} `
The
literals
array containing two elements will get to the
helper
function. In the first there will be the text
something
with a space after it, in the second - the empty line - that is, what is between the expression
${1 + 2 + 3}
and the end of the line. In the array
espressions
will be one element -
6
.
Here is a more complex example.
const string = helper`something another ${'x'} new line ${1 + 2 + 3} test`
Here, the following array will fall into the
helper
function, as the first parameter.
[ 'something\nanother ', '\nnew line ', '\ntest' ]
The second array will look like this.
[ 'x', 6 ]
Results
Today we talked about exception handling, about semicolon auto-substitution and about template literals in JavaScript. Next time we will look at some more important concepts of the language. In particular - work in strict mode, timers, mathematical calculations.
Dear readers! Do you use the capabilities of tagged templates in javascript?
