JavaScript: 12 questions and answers

Original author: Matt Crouch
  • Transfer
JavaScript is a terrific tool that can be found literally in every corner of the modern Internet. But even despite its incredible prevalence, and professionals in the field of JS will always have something to learn. There is always something that they do not know. In this article you will find an analysis of twelve questions about JavaScript, which even experienced developers often cannot answer. We will first look at ten typical questions, including those that often come up during interviews. The remaining two questions are devoted to more complex and ambiguous things, in particular, the use of JS to improve the performance of web pages and the development of applications that do not lose relevance over time.

image



Question number 1. What is prototype inheritance?


Almost everything in JavaScript is objects. Each object has a prototype from which it inherits properties and methods. If the object does not include the requested property, JavaScript will search for this property in the object's prototype. In this case, the search will be performed on a chain of prototypes until you find what you need. If the search fails, an error will be returned.

Prototypes are very useful for creating objects that have the same properties and methods. When all this is defined at the prototype level, only one copy of such entities is required, which leads to efficient use of memory.

var parent = {inherit: true}
var childA = Object.create(parent);
var childB = {};
Object.setPrototypeOf(childB, parent);
classchildCextendsparent{}

Prototypes can be added to objects when creating these objects using a command Object.create(), or after creation by a command Object.setPrototypeOf(). In the ES2015 standard, a keyword is provided class, there is also a command extendsthat allows you to use the value specified when it was called as a prototype object.

Question number 2. How can javascript be used to increase the availability of web projects?


Modern tools for ensuring the availability of websites for people with disabilities, often able to handle JavaScript and dynamic content pages. Both JS and dynamic data can be docked with accessibility tools, while keeping in mind that in this case it’s best when, for example, scripts are used as tools to extend the functionality of a project and not certain features that are absolutely necessary for its proper operation.

The usual way to help users work with the site is to provide convenient means to navigate through the objects of the pages with which you can interact. It's about focus management. For example, if a calendar appears on a page, the user should be able to use it without a mouse, in particular with the arrow keys. Namely, the left and right navigation arrows can be used (assuming that seven days are displayed in one line of the calendar screen display) for switching by day, and for moving up and down to switch between weeks. This is realized by listening to keyboard events while the calendar is getting focus.

If the change of important data is implemented by JavaScript, for example, when filling out a feedback form, new data should be sent to the screen reader. Often this is done by marking the corresponding container as an interactive area.

Question number 3. What is event float and how does it differ from event capture?



Event popup is used when implementing event delegations. If you subscribe to the events of the parent element, you can get information about the events and for its descendants.

Both the interception and the ascent of events are part of a process called “event distribution”, during which the browser responds to events occurring on the page. Older browsers performed either one or the other, but nowadays all browsers support both interception and event bubbling.

The first phase, the interception phase, is executed immediately after the event occurs. The event starts at the highest level, which is either an objectdocumentor an objectwindowdepending on the event. From here it is omitted when passing through the tag.<html>and through what is in this tag, until it reaches the element within which it originated.

Then there is the second phase - the ascent of the event. In its course the same process is repeated, but vice versa. It all starts with the element that triggered the event, it “pops up” to the root element <html>. When adding event listeners, this is the behavior expected for the system.

Question number 4. How does event delegation improve code on sites with a lot of interactive elements?


Websites are often full of dynamic elements that are constantly changing. If such elements must also be interactive, you will need some way of observing the events that occur when the user interacts with them. If each element needs its own event listener, it will litter the code and increase the load on the browser.

Event delegation is a technique that uses the event bubbling mechanism. By adding a listener to the parent element, the developer can adjust event handling for his descendants.

parentEl.addEventListener('click', function(e){
  if(e.target && e.target.nodeName == 'BUTTON') {
  // Щелчок по кнопке
  } });

Inside the event listener callback function, the target element of the event will be represented by a parameter targetthat can be used to decide on further actions. For example, the attribute of this parameter datacan store an identifier for accessing the properties of an object.

Question number 5. What are closures and how can they help code organization?


Functions in JavaScript use what is called "lexical scope". This means that they have access to variables defined in the scope that includes them, but those variables that are declared inside functions are not accessible from the outside.

functionouter() {
  let x = 'Web Designer';
  functionshout() {
  alert(`I love ${x}!`);
  }
  shout(); }

Calling the function outer()will display the message “I love Web Designer!”, But if you try to access the function shout()or variable xoutside the function outer(), it turns out that both are not defined. A closure is a combination of a function and its lexical environment. In our example, the closure is a function outer().

Closures are useful when creating large sets of components, since everything declared inside one closure does not affect others. Closures can be used to create private functions and variables in ways that resemble those used in other object-oriented languages ​​like Python. The “module” template makes extensive use of closures to provide structured ways for modules to interact.

Question number 6. What does the line 'use strict' at the top of the code block mean?


ES5 describes a special version of JavaScript, called strict mode (strict mode). In strict mode, the use of ambiguous constructs from earlier versions of the language causes errors instead of leading to unplanned behavior.

functionstrictFunction() {
  'use strict';
  myVar = 4; //ReferenceError }

In the code snippet above, we are trying to assign a value to an undeclared variable. Outside of strict mode, the execution of such a command will lead to the addition of a variable myVarto the global scope, which, if you do not pay enough attention to such things, can completely change the functionality of the script. In strict mode, this results in an error message, and thus prevents a possible disruption in the functioning of the program. The ES2015 modules use strict default mode, but in closures created using functions, the command 'use strict'can be used at the function level, as well as at the entire file level.

Question number 7. What does the term "raising variables" mean when applied to JavaScript?


One of the features of JavaScript is the fact that programs written on it are distributed in an uncompiled form. The browser compiles scripts, as they say, on the fly, and during this process takes notes on the functions and variables declared in these scripts.

After the first review of the code, the browser performs the second pass, which is the execution of the program, already knowing where functions and variables are applied. When executing a code snippet, declarations of functions and variables “rise” to the top of this fragment.

welcome("Matt"); //"Welcome, Matt."functionwelcome(name) {
  return`Welcome, ${name}.`;
}

In this example, the function welcome()can be used before its declaration in the code, since it “rises” to the top of the script.

Question number 8. How do arrow functions differ from ordinary functions?


Many changes have appeared in ES2015, and one of them, quite noticeable, was the introduction of switch functions.

functionCountUp() {
  this.x = 0;
  setInterval(() =>console.log(++this.x), 1000); }
var a = new CountUp();

The main difference between the switch functions and the usual functions, even if you don’t look at what they are shorter, is that the switch functions do not specify an eigenvalue for this. Instead, they use the value of the thisblock in which they are included. In the above example, when accessing this.xevery second, the numbers 1, 2, 3, and so on will be displayed. When used in a similar situation, a normal function thiswould be important undefined, which would lead to a conclusion NaN. The body of the arrow function is its return value. This makes it particularly convenient to use switch functions in promises. Normal functions, in contrast to the pointer, must explicitly return a certain value, otherwise it will automatically be returned undefined.

Question number 9. In what situations should the keywords let and const be used?


Another fundamental innovation in ES2015 was the introduction of keywords letand const, as alternative ways to declare variables. The scope of such variables is limited to the block in which they were declared. This gives more confidence that variables created in different blocks of code will not affect what is outside of these blocks.

for(let x=1; x<=3; x++) {
console.log(x); // 1, 2, 3}console.log(x); // "x is not defined"

If the value of a variable does not change during the execution of the program, use constinstead let. An attempt to redefine such a variable, which is more correctly called a "constant", will generate an error. It should be borne in mind that with this approach, the internal contents of objects and arrays, references to which are written in constants, can change, but they cannot be replaced with new objects.

Variables declared using letand constare not raised, unlike variables declared using a keyword var, so they cannot be accessed before they are initialized. The space between the beginning of a block of code and the place of initialization of a variable is known as the “temporary dead zone,” which can often be the cause of confusion.

Question number 10. What is functional programming and what are its features?



Pure Function

Functional programming is an approach to program development, the essence of which is that data representing the state of an application is processed exclusively by means of functions. If such functions do not produce side effects, the result is a code that is easy to work with and easy to understand.

Usually JS-projects are built using the principles of object-oriented programming. Information about the current state of the program is stored in objects, and if something changes on the page, information about changes is recorded in these objects.

Functional programming is, in fact, a different style of thinking. I must say that languages ​​like F # have used similar principles for a very long time. At the same time, in ES2015, some important mechanisms appeared that extend the possibilities of functional programming on JS.

If, when developing a web project, to adhere to the rules of functional programming, then you need to take into account that all operations must be performed inside the so-called "clean" functions. These are functions that are not affected by data that is outside the scope of these functions. In other words, when such a function is betrayed by the same data, it must always return the same result.

In addition, this means that functions should not share access to some external data in relation to them, for example, representing the state of the application. If an application needs to change its state, it should pass it to the function as a parameter.

Finally, in the code, you should avoid changing the existing values. For example, when performing operations involving changing objects, copies of these objects with modified values ​​should be returned. This helps to get rid of side effects that lead to errors and complicate code testing.

Question number 11. How to use javascript to improve webpage performance?


Today, the majority of web page views are performed from smartphones or tablets. In this case, not everyone has the most modern devices. Therefore, how quickly the pages respond to user actions is very important. Any "brakes" in the work of the site can result in the loss of a client. Fortunately, in JavaScript there are tools that help to avoid this.

▍ Avoid unnecessary effects on page scrolling.


“Ragged” scrolling is a clear sign that some program actions are being performed on the page. In some cases, the browser is forced to wait due to the fact that there are some event listeners on the page. So, events such as wheelor touchmovecan cancel scrolling, as a result, the page has to wait until the event is completed before the standard scrolling behavior begins.

This can lead to jumps when scrolling the page and to its unstable speed, which causes users to have bad impressions of working with the page.

document.addEventListener('touchmove', handler, {passive: true});

To avoid this, by adding an event listener, pass to it, as a third parameter, an object with the property passiveset to a value true. The browser, working with a similar event, may consider that it does not affect scrolling, as a result, the page scrolling can start without unnecessary delays.

The third parameter replaces the option useCapturein older browsers, so before applying this or that approach, you need to check what the browser supports. In order to intentionally disable the interaction of a certain area with the user, in most browsers you can use it touch-action: nonein CSS.

▍ Throttle events


Events, such as scrolling or resizing an element, occur as quickly as possible, so that all listeners get the actual data. If some resource-intensive operations are performed during the processing of each event, this can quickly lead to a page freeze.

const resizeDebounce = debounce(() => {
// Код для обработки события изменения размера }, 200);window.addEventListener('resize', resizeDebounce);

Eliminating the “bounce” of events is a technique that reduces the frequency of calling event-handling functions that occur too often. The implementation of this mechanism, as well as the frequency of calling functions in different projects vary, however, it can be noted that reducing the frequency of event processing to five times per second, for example, leads to an immediate improvement in page performance.

▍ Visible area of ​​the page


A typical way to use scrolling events is to detect when an element is visible on the page. Even with the use of the bounce technology, the challenge getBoundingClientRect()requires the browser to analyze the layout of the entire page. There is a new browser API, which is called IntersectionObserver, informing about the change of state of elements, which are monitored by means of it, calling a given function every time they enter or leave the viewport. For pages with endless scrolling, this API can be used to mark obsolete visual elements as suitable for deletion or reuse.

APIIntersectionObserverAvailable in all recent browsers with the exception of Safari. At the same time, the difference between using the new method of working with the page view area and the old approaches to determining the visibility of elements is quite noticeable.

▍Allocation of resource-intensive operations in separate streams


When working with large data sets or processing large files, such as images, JavaScript can block the browser window. By default, all tasks are performed in a single thread, so if this thread is overloaded, the application interface will no longer respond to user input.

If you know that it will take a long time to perform a certain operation, it would be nice to think about putting it into a web worker. So called scripts that run in separate threads, while even if these scripts perform resource-intensive operations, the user interface of the web application continues to work. Such scripts can transmit data to each other using a special messaging mechanism. Web workers have no access to the DOM and some object properties.window, messaging mechanisms are also used to transfer data to the main stream, which can affect the interface.

Question number 12. How to write JS-code that does not lose relevance over time?


One of the basic principles of JavaScript is that in the course of its development they try not to make, if feasible, changes that make it impossible to execute code written for its previous versions. As a result, for example, almost all the code written today will work a decade later, even taking into account the variability of the world of web development.

However, the fact that a certain code fragment is executed does not mean that it will not lose relevance over time. It’s time to ask how your post will look today in a few years. Here are some guidelines that will allow you to create programs that are ready for the test of the future.

▍ Avoid spaghetti code


At the initial stages of working on a certain project, it may seem an attractive idea, so to speak, to “throw everything in one pile” without breaking the code into small, fairly independent parts. Although this approach makes it easy to understand what is being done in a particular code fragment, it also means that the functionality of the program is closely related to each other. So, if somewhere in the program you need something that is implemented in another part of it, the corresponding section of the code is copied, and possibly rewritten, taking into account the new needs. If a new feature is added to the project, or an error is detected in it, each such copy that implements, essentially, the same thing, needs to be updated separately, which may require a lot of time.

If, from the very beginning of the project, to strive for modularity of the code, the functionality already implemented in it can be reused without problems. Any changes in a certain module are immediately reflected wherever it is accessed. Such programming techniques as the use of the template “module” can be applied gradually, without rewriting the rest of the project, which simplifies their use.

▍Try to avoid project dependency on frameworks.


Many modern frameworks, like React or Polymer, are pushing developers to develop modular code by creating components. In addition, each framework provides its own ways of developing components and organizing their interaction.

What happens when the next “best framework” appears on the horizon? Translating a project to a new framework, or even to a new version of the one used, may be difficult. In order to make the old components work in the new environment, it may take a lot of valuable time.

Therefore, wherever possible, it is worthwhile, instead of resorting to frameworks, to use regular JavaScript. With this approach, when changing the framework, the above problems can be minimized. If we talk about the practical implementation of this, then, for example, before transferring data to a component, you can use objects to process such data.

In addition, this approach contributes to the writing of a universal code. By avoiding, where possible, the use of browser-based APIs, the code can be made reusable in both browser and server projects, provided the latter are based on Node.

▍ Keep code clean


After the modules are written, their code should be not only workable, but also easy to read. Clean code means that even the one who sees it for the first time can understand this code, which, in particular, facilitates debugging of such code.

let activeUsers = users.filter(user => user.active === true);

Among the tools that can increase the likelihood that the code will not lose relevance in the future, self-documenting can be called. In particular, for example, the use of descriptive names for variables in iterators, instead of something like iand j, makes it easier to read the corresponding constructs.

In addition, and this is very important, you need to strive with all the forces for uniformity of the code. If the entire project is written using a single style, it is much easier to work with it. Therefore, before working on a project, it is worthwhile to develop a style guide and select tools, such as ESLint, that will help to adhere to this style.

▍Develop projects with regard to their possible growth.


The code with which it will be convenient to work should be well read. Similar ideas can be extended to the project structure. If, while working on a project, you do not adhere to reasonable rules for its structuring, it will be very difficult to manage such a project very soon.

If, at the very beginning of development, all files are in the same folder, this simplifies the matter. For example, when scripts import modules, you do not need to think about where exactly the sources of these modules are.

However, as the project grows, the number of files in its folder will increase, which ultimately makes it more difficult. Therefore, it is worth, from the very beginning, to support the structure of the project, which is well scaled. For example, all modules that are responsible for interacting with users can be stored in the folderusers, and so on. Here, of course, it is impossible to give a universal recommendation suitable for projects of all types. For example, when creating single-page applications, it is necessary to separate the logic of models, types and controllers. However, in another situation, something else may be needed. The structure is not a rigid structure imposed from above, but a natural reflection of the features of each particular development.

▍ Write the code that is suitable for testing.



Periodically perform tests with a tool like Jest to make sure everything works as it should.

Using frameworks like React helps create small components that are suitable for reuse. However, even with the use of a competent project structure, it may be difficult to verify that during the development of this project, all its parts work exactly as expected. The better the project code is covered with tests - the more likely it is that when it comes to the transition of this project from the development stage to the practical use stage, everything will work correctly.

Unit tests work at the module level. Tools like Mocha and Jest allow developers to check the program, asking some input data and the expected response of the system to the submission of this data. Periodic execution of such tests allows you to verify the correctness of the program and the absence of side effects during its execution.

Modules must be written so that they can be tested in isolation from the rest of the system. This means that each of them should have as few external dependencies as possible, and that they should not rely on a certain global state of the project.

There are, of course, other approaches to testing software projects. In particular, this integration and functional testing. The more fully such tests cover a project, the better the prospects for such a project in the future.

▍ In the language of tomorrow



Ensure that your code works in outdated browsers using a transpiler like Babel.

It is worth noting that the best way to ensure that the code is up to date is to write it using the syntax of the future. Although it may sound odd, like a call for a “leap into the unknown,” there are instruments that, without much difficulty, allow such a leap to be made.

For example, the Babel transpiler is a tool that can convert some forms of JavaScript into others. It is used to convert modern code into a format that not even the most modern browsers and other JS runtimes understand.

ES2015 brought to the world of JS a lot of opportunities that contribute to writing clean, understandable code. Among them are switch functions, promises and built-in support for modules. The latest language standard, ES2017, gives the developer even more convenience, thanks to, for example, asynchronous functions. Babel, provided it is properly configured, is able to convert all this into code that can be used today.

Ultimately, as new standards are introduced into the JS runtimes, projects will be able to do without a transpilation step, however, in order to remain as relevant as possible in the future, they should use tomorrow’s tools today.

Results


Today, we looked at twelve JavaScript questions and answers, many of which cover fairly extensive web development spaces and are designed to draw the attention of programmers to things that, in a daily routine, can go unnoticed for years. We hope the ideas voiced in this material will help you improve your development.

Dear readers! Recently, a lot of talk about the fact that JS-projects, if possible, should be written so that they depend on frameworks as little as possible. Do you think we should strive for this?

Also popular now: