JavaScript Guide Part 6: Exceptions, Semicolons, Template Literals

Original author: Flavio Copes
  • Transfer
  • Tutorial
The topics in this part of the JavaScript tutorial translation will be exception handling, automatic semicolon features, and template 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, template literals
Part 7: strict mode, this keyword, events, modules, mathematical calculations
Part 8: overview of ES6 standard features
Part 9: Overview of ES7, ES8, and ES9 Standards



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, when they occur, the program 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)) //message text
console.log('Before') //Before
console.log(fn(notObj)) //ошибка, выполнение программы останавливается
console.log('After')

Here we have a function that we plan to use to process objects that have a property value. She returns this property. If you use this function for its intended purpose, that is, to transfer to it such an object that it is designed to work with, no errors will be generated when it is executed. But if you pass something inappropriate to it, in our case, a declared but uninitialized variable, then an error will occur when you try to access the property of the valuevalue undefined. An error message will appear in the console, program execution will stop.

Here's what it looks like when you run this code in Node.js.


TypeError exception in Node.js

If something like this occurs 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 web server code, this behavior is extremely undesirable. It would be nice to have a mechanism that allows, without stopping the program, to catch the error, and then take measures to correct it. Such a mechanism exists in JavaScript; it is represented by a design try...catch.

▍construction try ... catch


The design try...catchallows you to catch and handle exceptions. Namely, it includes a block trythat includes code that can cause an error, and a block catchinto which control is transferred when an error occurs. The blocks trydo not include absolutely all program code. Those parts of it that can cause runtime errors are placed there. For example, calls to functions that have to work with certain data received from external sources. If the structure of such data is different from what the function expects, an error may occur. Here's what the design diagram looks like try...catch.

try {
  //строки кода, которые могут вызвать ошибку
} catch (e) {
  //обработка ошибки
}

If the code is executed without errors, the block catch(exception handler) is not executed. If an error occurs, the error object is transferred there and some actions are taken to combat this error.

We apply this construction in our example, protecting with its help dangerous sections of the program - those in which the function is called fn().

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') //Before
try {
    console.log(fn(notObj))
} catch (e) {
    console.log(e.message) //Cannot read property 'value' of undefined
}
console.log('After') //After

Let's look at the results of executing this code in Node.js.


Error Handling in Node.js

As 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 after it. We “process” the error by simply printing to the console the values ​​of the property of an messageobject of type Error . What will be the handling of the error that occurred in the actually used code depends on the error.

We discussed the block above try...catch, but, in fact, this design includes another block - finally.

▍ finally block


A block finallycontains code that is executed regardless of whether or not an error has occurred in the code running in the block try. Here is how it looks.

try {
  //строки кода
} catch (e) {
  //обработка ошибки
} finally {
  //освобождение ресурсов
}

A block finallycan also be used if there is try...catch...finallyno block in the block catch. With this approach, it is used in the same way as in a construction with a block catch, for example, to free up resources occupied in a block try.

▍ Nested try blocks


Blocks trycan be nested into each other. In this case, the exception is processed in the nearest block catch.

try {
  //строки кода
  try {
    //другие строки кода
  } finally {
    //ещё какой-то код
  }
} catch (e) {
}

In this case, if an exception occurs in the indoor unit try, it will be processed in the outdoor unit catch.

▍ Self-generated exception


Exceptions can be generated independently using the instructions throw. Here is how it looks.

throw value

After this instruction is executed, control is transferred to the nearest block catch, or if such a block cannot be found, the program stops. The exception value can be anything. For example, a user-defined error object.

About semicolons


Using semicolons in JavaScript is optional. Some programmers do without them, relying on an automatic arrangement system for them, and placing them only where it is absolutely necessary. Some people prefer to place them wherever possible. The author of this material refers to the category of programmers who want to do without semicolons. He says that he decided to do without them in the fall of 2017 by setting Prettier to delete them wherever you can do without their explicit insertion. In his opinion, code without semicolons looks more natural and easier to read.

Perhaps we can say that the community of JS developers is divided, in relation to semicolons, into two camps. At the same time, there are also JavaScript style guides that prescribe explicit semicolons, and guides that recommend doing without them.

All this is possible due to the fact that JavaScript has a system for automatic semicolon 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 the semicolons are placed automatically when preparing the 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 automatic semicolons


The JavaScript parser automatically adds semicolons when parsing program text in the following situations:

  1. When the next line starts with a code that interrupts the current code (the code of a certain command may be located on several lines).
  2. When the next line begins with a character }that closes the current block.
  3. When the end of the file with the program code is detected.
  4. In a line with the command return.
  5. In a line with the command break.
  6. In a line with the command throw.
  7. In a line with the command continue.

▍ Examples of code that does 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 the execution of the following code fragment?

const hey = 'hey'
const you = 'hey'
const heyYou = hey + ' ' + you
['h', 'e', 'y'].forEach((letter) => console.log(letter))

When you try to execute this code, an error will be generated, the Uncaught TypeError: Cannot read property 'forEach' of undefinedsystem, 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 penultimate line of the first example.

Here is another piece of code.

(1 + 2).toString()

The result of its execution will be the output of the string "3". But what happens if something like this appears in the next code snippet?

const a = 1
const b = 2
const c = a + b
(a + b).toString()

In this situation, an error will appear TypeError: b is not a functionsince the above code will be interpreted as follows.

const a = 1
const b = 2
const c = a + b(a + b).toString()

Let us now take a look at an example based on rule 4.

(() => {
  return
  {
    color: 'white'
  }
})()

You might think that this IIFE will return an object containing a property color, but in reality it is not. Instead, the function will return a value undefinedsince the system adds a semicolon after the command return.

In order to solve a similar problem, the opening brace of the object literal must be placed on the same line as the command return.

(() => {
  return {
    color: 'white'
  }
})()

If you look at the following code fragment, you might think that it will display in the message box 0.

1 + 1
-1 + 1 === 0 ? alert(0) : alert(2)

But it outputs 2, because, in accordance with rule No. 1, this code is represented as follows.

1 + 1 -1 + 1 === 0 ? alert(0) : alert(2)

You should be careful when using semicolons in JavaScript. You can meet both ardent supporters of semicolons, and their opponents. In fact, when deciding whether semicolons are needed in your code, you can rely on the fact that JS supports their automatic substitution, but everyone must decide for themselves whether they are needed in his code or not. The main thing is to apply the chosen approach consistently and reasonably. Regarding the placement of semicolons and the structure of the code, we can recommend the following rules:

  • Using the command return, arrange what it should return from the function on the same line as the command. The same applies to teams break, throw, continue.
  • Pay special attention to situations where a new line of code begins with a bracket, since this line can be automatically combined with the previous one and presented by the system as an attempt to call a function or an attempt to access an array element.

In general, it can be said that whether you put semicolons yourself, or rely on their automatic placement, test the code in order to make sure that it works exactly as expected.

Quotation marks and wildcard literals


Let's talk about the features of using quotes in JavaScript. Namely, we are talking about the following types of quotes allowed in JS programs:

  • Single quotes.
  • Double quotes.
  • Back 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 strings enclosed in single quotes, you need to escape the character of a single quotation mark, and in strings enclosed in double quotes, the character is double.

const test = 'test'
const test = 'te\'st'
const test = 'te"st'
const test = "te\"st"
const test = "te'st"

In different style guides you can find both a recommendation for using single quotes and a recommendation for using double quotes. The author of this material says that in JS-code he strives to use exclusively single quotes, using double-quotes only in HTML-code.

Backticks appeared in JavaScript with the release of the ES6 standard in 2015. They, among other new features, make it possible to conveniently describe multi-line strings. Such strings can also be specified using regular quotation marks — using an escape sequence \n. It looks like this.

const multilineString = 'A string\non multiple lines'

Inverted commas (usually the button for entering them is located to the left of the number 1 key on the keyboard) do without \n.

const multilineString = `A string
on multiple lines`

But the possibilities of backquotes are not limited to this. So, if a string is described using backquotes ${}, you can substitute values in it using the construct that result from the calculation of JS expressions.

const multilineString = `A string
on ${1+1} lines`

Such strings are called template literals.

Template literals have the following features:

  • They support multi-line text.
  • They make it possible to interpolate strings; built-in expressions can be used 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 features.

▍Multiline text


When setting multi-line texts with backquotes, you need to remember that spaces in such texts are just as important as other characters. For example, consider the following multiline text.

const string = `First
                Second`

His conclusion will give approximately the following.

First
                Second

That is, it turns out that when this text was entered in the editor, then perhaps the programmer expected that the words Firstand Second, when outputting, would appear strictly under each other, but in reality this is not so. In order to get around this problem, you can start multiline text with a line feed, and, immediately after the closing back quote, call a method trim()that will remove whitespace at the beginning or end of the line. Such characters, in particular, include spaces and tabs. The end of line characters will also be deleted.

It looks like this.

const string = `
First
Second`.trim()

▍ Interpolation


By interpolation here we mean the conversion of variables and expressions into strings. This is done using the design ${}.

const variable = 'test'
const string = `something ${ variable }` //something test

You ${}can add anything to the block — even expressions.

const string = `something ${1 + 2 + 3}`
const string2 = `something ${foo() ? 'x' : 'y' }`

The stringtext will get something 6into the constant , either the text or the text string2will be written to the constant . It depends on whether the function returns true or false value (the ternary operator is used here, which, if what is before the question mark is true, returns what comes after the question mark, otherwise returning what comes after the colon )something xsomething yfoo()

▍Tagged Templates


Tagged templates are used in many popular libraries. Among them are Styled Components , Apollo , GraphQL .

What such patterns output is subject to some logic defined by the function. Here is a slightly revised example in one of our publications illustrating how to work with tagged template strings.

const esth = 8
function helper(strs, ...keys) {
  const str1 = strs[0] //ES
  const str2 = strs[1] //is
  let additionalPart = ''
  if (keys[0] == 8) { //8
    additionalPart = 'awesome'
  }
  else {
    additionalPart = 'good'
  }
  return `${str1}${keys[0]}${str2}${additionalPart}.`
}
const es = helper`ES ${esth} is `
console.log(es) //ES 8 is awesome.

Here, if a esthnumber is written in a constant 8, a esstring will be entered ES 8 is awesome. Otherwise, there will be another line. For example, if there esthis a number in 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;
`;

At Apollo, they are used to define GraphQL queries.

const query = gql`
  query {
    ...
  }
`

Knowing how to construct tagged templates, it is easy to understand that styled.button, and gqlearlier example - it's just a function.

function gql(literals, ...expressions) {
}

For example, a function gql()returns a string that can be the result of any calculation. The parameter of literalsthis function is an array containing the contents of a template literal broken into parts, and expresionscontains the results of evaluating expressions.

Let's parse the next line.

const string = helper`something ${1 + 2 + 3} `

An helperarray literalscontaining two elements gets into the function . The first will be text somethingwith a space after it, the second will be an empty line - that is, that which is between the expression ${1 + 2 + 3}and the end of the line. There espressionswill be one element in the array - 6.
Here is a more complex example.

const string = helper`something
another ${'x'}
new line ${1 + 2 + 3}
test`

Here in the function helper, as the first parameter, the next array will get.

[ 'something\nanother ', '\nnew line ', '\ntest' ]

The second array will look like this.

[ 'x', 6 ]

Summary


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?


Also popular now: