📜 ⬆️ ⬇️

JavaScript Guide, Part 7: strict mode, this keyword, events, modules, mathematical calculations

Today, in the seventh part of the translation of the JavaScript manual, we will talk about executing code in strict mode, about the features of the this keyword, about events, about modules, about mathematical calculations. Here we will touch on the topic of work with timers and asynchronous programming.

→ 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



Strict regime


Strict mode (strict mode) appeared in the standard ES5. In this mode, the semantics of the language changes, it is aimed at improving the behavior of JavaScript, which leads to the fact that the code in this mode does not behave like normal. In fact, we are talking about the fact that this mode eliminates the shortcomings, ambiguities of the language, outdated features that remain in it for compatibility reasons.
')

â–ŤInclude strict mode


In order to use strict mode in some code, it must be explicitly enabled. That is, we are not talking about the fact that this mode is applied by default. Such an approach would disrupt the work of countless existing programs based on the mechanisms of the language that have been present in it since the very beginning, since 1996. In fact, the significant efforts of those who develop JavaScript standards are aimed precisely at ensuring compatibility, so that the code written for old versions of standards can be executed on today's JS engines. This approach can be considered one of the keys to the success of JavaScript as a language for web development.

In order to enable strict mode, a special directive is used, which looks like this.

'use strict' 

In addition, the directive, written as "use strict" , and the same directive, followed by a semicolon ( 'use strict'; and "use strict"; ), will also have the effect. This directive (exactly like this, along with quotes), so that all code in a certain file would be executed in strict mode, is placed at the beginning of this file.

 'use strict' const name = 'Flavio' const hello = () => 'hey' //... 

Strict mode can also be enabled at the level of a single function. For this, the corresponding directive must be placed at the beginning of the code of the function body.

 function hello() { 'use strict' return 'hey' } 

This can be useful if you need to use strict mode in the existing code base and, at the same time, including it at the file level is impractical due to lack of time to thoroughly test the code of the entire file.

It should be noted that if strict mode is turned on, you cannot turn it off during program execution.

Consider some of the features of strict mode.

â–Ť Fighting random initialization of global variables


We have already said that if you accidentally assign some value to an undeclared variable, even if you do this in the function code, such a variable will be made global by default (belonging to the global object). This can lead to surprises.

For example, the following code creates exactly such a variable.

 ;(function() { variable = 'hey' })() 

The variable will be available in the global scope after running IIFE.

If you enable strict mode at the level of this function, the same code will cause an error.

 ;(function() { 'use strict' variable = 'hey' })() 

â–ŤErrors occur during the assignment of values


JavaScript, in its normal mode, does not report any errors that occur during the assignment of values.

For example, JS has the value undefined , which is one of the primitive values ​​of the language and is represented by the property of the global object undefined . In the usual JS such a command is quite possible.

 undefined = 1 

It looks like writing a unit to some variable named undefined , but in reality it is an attempt to write a new value to a property of a global object, which, by the way, according to the standard, cannot be overwritten. In normal mode, although such a command is possible, it will lead to nothing - that is, the value of undefined will not be changed, and the error message will not appear. In strict mode, this will cause an error. In order to see this error message, and at the same time make sure that the value undefined not overridden in the normal mode, try the following code in a browser or in Node.js.

 undefined = 1 console.log('This is '+undefined) ;(() => { 'use strict' undefined = 1 })() 

The same system behavior is typical when working with such entities as the values ​​of Infinity and NaN , as well as with other similar ones. Strict mode allows you to avoid all this.

In JavaScript, you can set object properties using the Object.defineProperty () method. In particular, using this method, you can set properties that cannot be changed.

 const car = {} Object.defineProperty(car, 'color', {  value: 'blue',  writable: false }) console.log(car.color) car.color = 'yellow' console.log(car.color) 

Notice the writable: false attribute used when setting the color property.

The above code, executed in normal mode, will not lead either to a change in the property of the color object or to an error output. An attempt to change this property in strict mode will end with an error.

 ;(() => { 'use strict' car.color = 'red' })() 

The same applies to getters. This code will execute, albeit to no avail.

 const car = { get color() {   return 'blue' } } console.log(car.color) car.color = 'red' console.log(car.color) 

And an attempt to do the same in strict mode will cause an error reporting an attempt to set a property of an object that has only a getter.

 ;(() => { 'use strict' car.color = 'yellow' } )() 

In JavaScript, there is an Object.preventExtensions () method that makes an object non-extensible, that is, one to which new properties cannot be added. When working with such objects in the usual mode, the same features of the language that we considered above appear.

 const car = { color: 'blue' } Object.preventExtensions(car) console.log(car.model) car.model = 'Fiesta' console.log(car.model) 

Here, both attempts to display the property of the model object will result in the console undefined value undefined . There was no such property in the object; an attempt to create it after the object was made non-expandable did not lead to anything. The same action in strict mode results in an error message.

 ;(() => { 'use strict' car.owner = 'Flavio' } )() 

In the same category of actions that do not lead to any changes, possibly expected by the programmer, but not causing errors, there are operations in the course of which attempts are made to assign certain properties to primitive values. For example, such code, in the usual mode, will not cause an error, but will not give any results.

 let one = 1 one.prop = 2 console.log(one.prop) 

The same thing in strict mode will result in an error message indicating that the number 1 cannot create the prop property. Similarly, the system behaves when working with other primitive data types.

â–ŤError associated with the removal of entities


In normal mode, if you try to delete, using the operator delete , the property of an object that cannot be deleted, delete simply returns false and everything will silently end in failure.

 delete Object.prototype 

In strict mode, an error will be displayed here.

â–Ť Function arguments with the same name


Functions may have parameters with the same name, it does not cause errors (although this looks like an error of who created such a function).

 ;(function(a, a, b) { console.log(a, b) })(1, 2, 3) //2 3 

This code in the normal mode displays in the console 2 3 . In strict mode, this will cause an error.

By the way, if at the declaration of the arrow function its parameters will have the same name, this, in the normal mode, will result in an error message.

Octal values


In normal JavaScript, you can use octal values ​​by adding the number 0 to the beginning.

 ;(() => { console.log(010) })() //8 

Here, the decimal representation of the octal number 10 , that is 8 will fall into the console. This 0 before the number can be set randomly. In strict mode, octal numbers specified in this format cannot be operated. But if you need to use strict mode and work with octal numbers, you can write them in the format 0oXX . The following code will also output 8 .

 ;(() => { 'use strict' console.log(0o10) })() //8 

â–ŤOperator with


The operator with , the use of which can lead to confusion, is strictly prohibited.
Changing the behavior of the code in strict mode is not limited to those discussed above. In particular, in this mode, the keyword this , which we have already encountered, behaves differently and which we will now discuss in more detail.

Keyword Features


The keyword this , or execution context, allows us to describe the environment in which JS code is executed. Its value depends on the place of its use and on whether strict mode is enabled or not.

The key word this is in strict mode


In strict mode, the value of this passed to the function is not cast to the object. This transformation not only requires resources, but also gives functions access to the global object if they are called with this set to undefined or null . This behavior means that the function can gain unauthorized access to the global object. In strict mode, the conversion of this to the function is not performed. In order to see the difference between this behavior in functions in different modes - try this code using the 'use strict' directive and without it.

 ;(function() { console.log(this) })() 

â–ŤThe keyword this in object methods


A method is a function referenced in an object property. The keyword this in such a function refers to this object. This statement can be illustrated by the following example.

 const car = { maker: 'Ford', model: 'Fiesta', drive() {   console.log(`Driving a ${this.maker} ${this.model} car!`) } } car.drive() //Driving a Ford Fiesta car! 

In this case, we use the normal function (and not the arrow function - this is important), the keyword this , used in which, is automatically attached to the object containing this function.

Note that the above method for declaring an object's method is similar to this:

 const car = { maker: 'Ford', model: 'Fiesta', drive: function() {   console.log(`Driving a ${this.maker} ${this.model} car!`) } } 

The same behavior of the keyword this in the object method can also be observed using the following construct.

 const car = { maker: 'Ford', model: 'Fiesta' } car.drive = function() { console.log(`Driving a ${this.maker} ${this.model} car!`) } car.drive() //Driving a Ford Fiesta car! 

â–ŤThe keyword this and arrow functions


Let's try to rewrite the above example using the arrow function as an object method.

 const car = { maker: 'Ford', model: 'Fiesta', drive: () => {   console.log(`Driving a ${this.maker} ${this.model} car!`) } } car.drive() //Driving a undefined undefined car! 

As you can see, here, instead of the names of the car manufacturer and its model, the values undefined are displayed. The fact is that, as we have said, this in the arrow function contains a link to the context that includes the function.

You cannot bind this to an arrow function, and you can

â–ŤPartition this


In JavaScript, there is such a thing as a binding this . This can be done in different ways. For example, when declaring a function, you can bind its this keyword to an object using the bind() method.

 const car = { maker: 'Ford', model: 'Fiesta' } const drive = function() { console.log(`Driving a ${this.maker} ${this.model} car!`) }.bind(car) drive() //Driving a Ford Fiesta car! 

Using the same method to the method of one object, as this , you can bind another object.

 const car = { maker: 'Ford', model: 'Fiesta', drive() {   console.log(`Driving a ${this.maker} ${this.model} car!`) } } const anotherCar = { maker: 'Audi', model: 'A4' } car.drive.bind(anotherCar)() //Driving a Audi A4 car! 

This binding can also be organized at the stage of calling a function using the call() and apply() methods.

 const car = { maker: 'Ford', model: 'Fiesta' } const drive = function(kmh) { console.log(`Driving a ${this.maker} ${this.model} car at ${kmh} km/h!`) } drive.call(car, 100) //Driving a Ford Fiesta car at 100 km/h! drive.apply(car, [100]) //Driving a Ford Fiesta car at 100 km/h! 

this bound to what is passed to these methods as the first argument. The difference between these methods is that apply() , as the second argument, takes an array with the arguments passed to the function, and call() takes a list of arguments.

â–ŤAbout this binding in browser event handlers


In callback event handlers, this points to the HTML element with which this or that event occurred. In order to bind to a callback, as this , something else, you can use the bind() method. Here is an example illustrating this.

 <!DOCTYPE html> <html> <body>   <button id="el">Element (this)</button>   <button id="win">Window (this</button>   <script>     const el = document.getElementById("el")     el.addEventListener('click', function () {         alert(this) //object HTMLButtonElement     })     const win = document.getElementById("win")     win.addEventListener('click', function () {         alert(this) //object Window     }.bind(this))   </script> </body> </html> 

Developments


JavaScript in the browser uses an event-based programming model. Certain actions are performed by the code in response to events that occur. In this section we will talk about events and how to handle them.

The event can be, for example, the completion of loading a DOM, data acquisition, made as a result of an asynchronous request, mouse click on a page element, scrolling a page, entering a certain character from the keyboard. Actually, there are a lot of events, processing which, the JS-code of the page allows to solve a wide range of tasks on the application's interaction with users, with the page elements, with the environment in which the code works.

Event Handlers


You can respond to events using event handlers, which are functions that are called when events occur.
If necessary, several handlers can be registered to handle the same event, which will be called if this event occurs. You can register event handlers in various ways. Consider three such ways.

Built-in event handlers


These days, built-in event handlers are rarely used because of their limitations. Previously, they were used much more often. To set such an event handler, its code is added to the HTML markup of the element as a special attribute. In the following example, such a simple event handler for the onclick event that occurs when a button is clicked is assigned to a button labeled Button 1 .

 <!DOCTYPE html> <html> <body>   <button onclick="alert('Button 1!')">Button 1</button>   <button onclick="doSomething()">Button 2</button>   <script>       function doSomething(){           const str = 'Button 2!'           console.log(str)           alert(str)       }       </script> </body> </html> 

The HTML code for Button 2 takes a similar approach, but it indicates the function that is executed in response to the button being pressed. This code outputs the specified string to the console and displays a window with the same text.

â–Ť Assignment of the handler to the property of the HTML element


This method of assigning event handlers is suitable for cases where a certain element event should have only one handler. It consists in assigning the function to the corresponding property of the element.

For example, the window object has an onload that is triggered after loading the HTML code of the page and all the additional resources it needs, for example, styles and images. If you assign a handler to this event, then when you call it, you can be sure that the browser has loaded all the contents of the page, with which you can now work programmatically, without fear that some page elements have not yet been loaded.

 window.onload = () => {   alert('Hi!') //   } 

This approach is often used in the processing of XHR requests. For example , during a request setup, you can set its onreadystatechange event handler , which will be called when its readyState property changes state. Here is an example of using this approach to load JSON data from a public API.

 <!DOCTYPE html> <html> <body>   <button onclick="loadData()">Start</button>   <script>       function loadData (){           const xhr = new XMLHttpRequest()           const method = 'GET'           const url = 'https://jsonplaceholder.typicode.com/todos/1'           xhr.open(method, url, true)           xhr.onreadystatechange = function () {               if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {                   console.log(xhr.responseText)               }           }           xhr.send()      }       </script> </body> </html> 

It is possible to check whether the handler is assigned to a certain event.

 if (window.onload){} 

â–ŤUsing addEventListener () method


The addEventListener() method, which we have already met, is a modern mechanism for assigning event handlers. It allows you to register multiple handlers for one event.

 window.addEventListener('load', () => { //  }) 

Please note that IE8 (and older versions of it) do not support the addEventListener() method. It uses a similar attachEvent() method. This should be taken into account if your program must support legacy browsers.

â–ŤAbout assigning event handlers to various elements


You can connect event handlers to the window object to handle "global" events, such as keystrokes on the keyboard. At the same time, individual HTML elements are assigned event handlers that react to what happens to these elements, for example, mouse clicks. Therefore, the addEventListener() method is used with both the window object and ordinary elements.

Event Event Object


As the first parameter, the event handler can take an event object - Event . The set of properties of this object depends on the event that it describes. Here, for example, is a code that demonstrates handling keyboard keystroke events keydown window keydown event.

 <!DOCTYPE html> <html> <body>   <script>       window.addEventListener('keydown', event => {           //              console.log(event.type, event.key)       })       window.addEventListener('mousedown', event => {           //             //0 -  , 2 -            console.log(event.type, event.button, event.clientX, event.clientY)       })   </script> </body> </html> 

As you can see, here, to display information about the pressed key to the console, the property of the key object is used. The type property is also used here, indicating the type of event. In this example, in fact, we are working with a KeyboardEvent object, which is used for descriptions of keyboard-related events. This object is an inheritor of the Event object. Objects designed to handle a variety of events extend the capabilities of the standard event object.

In the same example, the MouseEvent object is used to handle mouse-related events. In the mousedown event handler, we display the event type, button number ( button property) and pointer coordinates at the time of the click ( clientX and clientY ) to the clientY .

The DragEvent object is used when handling events that occur while dragging page elements.

Among the properties of the Event object that are available in other event objects are the already mentioned type property and the target property, indicating the DOM element on which the event occurred. The Event object has methods. For example, the createEvent() method allows you to create new events.

spot events


Consider the following example.

 <!DOCTYPE html> <html>   <head>       <style>           #container {               height: 100px;               width: 200px;               background-color: blue;           }           #child {               height: 50px;               width: 100px;               background-color: green;           }       </style>   </head>   <body>   <div id="container">       <div id="child">       </div>   </div>   <script>       const contDiv = document.getElementById('container')       contDiv.addEventListener('click', event => {           console.log('container')       })       const chDiv = document.getElementById('child')       chDiv.addEventListener('click', event => {           console.log('child')       })       window.addEventListener('click', event => {           console.log('window')       })       </script> </body> </html> 

If you open the download page with the code in the browser, open the console and click the mouse first in the free area of ​​the page, then on the blue rectangle, and then on the green, then the console will get the following:

 window container window child container window 


Event float

What can be observed here is called event bubbling. Namely, an event that occurs in a child element extends to the parent element. This process continues until the event reaches the topmost element. If the elements on which the pop-up event passes have corresponding handlers defined, they will be called according to the order of event propagation.

Event stopPropagation() can be stopped using the stopPropagation() method of the stopPropagation() object. For example, if it is necessary that, after clicking the child element, the corresponding event does not extend further, we need to rewrite the code in which we assign it a click event handler, as follows.

 chDiv.addEventListener('click', event => {   console.log('child')   event.stopPropagation() }) 

, , — , — container , — child , .

 window container window child 

â–Ť


, .

load


load window . , , HTML- body .


click . dblclick — . click dblclick , click , — dblclick . mousedown , mousemove , mouseup , . , mousemove , , , . , - , . .


keydown . , . — keyup .

scroll


scroll window . , , , window.scrollY .

, , mousemove , .


mousemove scroll . - . . «» (throttling), Lodash . , , , , . .

 let cached = null window.addEventListener('mousemove', event => {   if (!cached) {       setTimeout(() => {           //     cached           console.log(cached.clientX, cached.clientY)           cached = null           }, 100)   }   cached = event }) 

, mousemove , 100 .

ES-


ES6 , ES-. , , -, , Node.js, .

, . , , . , , .

Node.js CommonJS. , ES-, , . , , ES-, , , , . , caniuse.com , 2018 ES- 80%.

ES- Node.js.

â–Ť ES-


Node.js ES- .

 import package from 'module-name' 

CommonJS- .

 const package = require('module-name') 

, JavaScript-, - . This is done using the export keyword. , , , , uppercase.js . .

 export default str => str.toUpperCase() 

, . .
( , ) .

HTML- , <script> type="module" .

 <script type="module" src="index.js"></script> 

, (defer) . , , uppercase.js , , , . -. , -. , VSCode, Live Server ( — ritwickdey.liveserver).

 <!DOCTYPE html> <html> <head> </head> <body>   <script type="module">     import toUpperCase from './uppercase.js'     console.log(toUpperCase('hello'))   </script> </body> </html> 

HELLO .

URL.

 import toUpperCase from 'https://flavio-es-modules-example.glitch.me/uppercase.js' 

, , ./ / .

â–Ť


, .

 export default str => str.toUpperCase() 

.

 const a = 1 const b = 2 const c = 3 export { a, b, c } 

module.js , , , .

 <html> <head> </head> <body>   <script type="module">     import * as m from './module.js'     console.log(ma, mb, mc)   </script> </body> </html> 

1 2 3 .

, , .

 import { a } from './module.js' import { a, b } from './module.js' 

, , .

 console.log(a) 

:

 import { a, b as two } from './module.js' 

, , , , . module.js .

 const a = 1 const b = 2 const c = 3 export { a, b, c } export default () => console.log('hi') 

.

 import sayHi, { a } from './module.js' console.log(a) sayHi() 

.

â–ŤCORS


CORS . , CORS, ( Access-Control-Allow-Origin: * ).

â–Ť nomodule


, , , script , nomodule . , , .

 <script type="module" src="module.js"></script> <script nomodule src="fallback.js"></script> 

â–Ť ES6 WebPack


ES6 — , , ECMAScript. , , , , . , WebPack, , , .

â–Ť CommonJS


, Node.js CommonJS. , . CommonJS npm.

CommonJS-, , . , up-node.js .

 exports.uppercase = str => str.toUpperCase() 

, .

 const up = require('./up-node.js') console.log(up.uppercase('hello')) 

HELLO .

, npm, , .

 const package = require('module-name') 

CommonJS , . .

CommonJS- .

 exports.a = 1 exports.b = 2 exports.c = 3 

, .

 const { a, b, c } = require('./up-node.js') 


, JavaScript — . Math , . , , JS- .

â–Ť


(+)


+ . :

 const three = 1 + 2 //3 const four = three + 1 //4 

, , , .

 'three' + 1 // three1 

(-)


 const two = 4 - 2 //2 

(/)


.

 20 / 5 //4 20 / 7 //2.857142857142857 

0, . Infinity ( ) - Infinity ( ).

 1 / 0 //Infinity -1 / 0 //-Infinity 

(%)


% , .

 20 % 5 //0 20 % 7 //6 

0 NaN (Not a Number — ).

 1 % 0 //NaN -1 % 0 //NaN 

(*)


 1 * 2 //2 -1 * 2 //-2 

(**)


, .

 1 ** 2 //1 2 ** 1 //2 2 ** 2 //4 2 ** 8 //256 8 ** 2 //64 

â–Ť


(++)


++ 1 . .

— 1 , .

 let x = 0 ++x //1 x //1 

— , .

 let x = 0 x++ //0 x //1 

(--)


-- ++ , 1 , .

 let x = 0 x-- //0 x //-1 --x //-2 

(-)


.

 let x = 2 -x //-2 x //2 

(+)


, , . — .

 let x = 2 +x //2 x = '2' +x //2 x = '2a' +x //NaN 

â–Ť


JavaScript, ( = ), , . , , += .

 let x = 2 x += 3 x //5 

: « , , , , ». , .

 let x = 2 x = x + 3 x //5 

:


â–Ť


. , .

 const a = 1 * 2 + 5 / 2 % 2 

2.5 . , . , .


, , . , .

 const a = 1 * 2 + 5 / 2 % 2 const a = 2 + 2.5 % 2 const a = 2 + 0.5 const a = 2.5 

. .

 const a = 1 * (2 + 5) / 2 % 2 

1.5 .

â–Ť Math


Math , . . , .

, , Math.E — , e, Math.PI — , π.

 Math.E // 2.718281828459045 Math.PI //3.141592653589793 

.


â–Ť


JavaScript , .


, . , , , .


.


, .

 1 === true //false 1 == true //true 

, , 1 true . 1 true , 1 true .


, , . Node.js. , :


PDF- Node.js.

Results


, this, , , . , ES6.

Dear readers! , JavaScript?

Source: https://habr.com/ru/post/431072/


All Articles