📜 ⬆️ ⬇️

JavaScript FAQ: Part 1

image

A few days ago, TheShock and I created a topic in which we collected your questions about JavaScript (architecture, frameworks, problems). It is time to respond to them. We received a lot of questions, both in the comments and by email. This first part of the answers is the questions that got to me.

1. Prototype inheritance. How does everything work?

I have a problem with understanding the prototype model, I got used to the “classical” class :) but decided to study JS. Please, if possible, write an article where, in the form of patterns, explain the possible options for constructing “classes”, different levels of visibility of methods and variables. I understand that such articles can be found in a huge number of ve, I understand that in JS, levels of visibility are “not needed”.

The answer was very long, so I created a separate topic: Basics and Misconceptions About JavaScript

2. What is the most convenient model for creating objects?

If with new, then how are you protected from errors:
1. Functions constructors I always write with a capital letter;
2. Check the validity of the creation through this instanceof Function_Name (I avoid this instanceof arguments.callee for performance reasons)
3. Similar to the second one, but I check with the window, since I do not want to hardcode the name and do not write scripts for out-of-browser environments.

Better, more familiar and ideological to create objects through new. Designers should be called with a capital letter.
I prefer to be based on agreements and do not check this inside the constructor - the constructor called without new and therefore leaked to the globals - it means “fool himself”. And in no case I do not encourage an error with new - some check if this is a global means the user has called a constructor without new and creates an object inside the constructor and returns it - this is an error promotion and an ideologically incorrect approach.
var Obj = function () { "use strict"; this.pew = 100; }; //  new Obj.pew++; //    Obj(); // TypeError: this is undefined 

new is not acceptable for factory methods, and short constructors - jQuery
')
I summarize the code:
 // :  ,   , use strict var Obj = function () { "use strict"; this.pew = 100; }; //   :      var Obj = function () { if (!(this instanceof Obj)) { return new Obj(); } this.pew = 100; }; 

3. How to determine which mouse button is pressed on JS?


The mousedown mouseup event mousedown mouseup triggered by all mouse buttons, the click is just the left one. In the event handler, you need to check the code of the event.button button to find out which one was pressed:
(0 - Left, 1 - Medium, 2 - Right). In IE8, everything is wrong, see the code:
 var button = document.getElementById('button'), // 0 1 2 buttonMap = ['Left', 'Middle', 'Right'], handler = function (event) { event = event || window.event; alert(buttonMap[event.button] + ' id: ' + event.button); }; if (button.addEventListener) { button.addEventListener('mousedown', handler, false); } else { // IE 0 1 2 3 4 buttonMap = ['???', 'Left', 'Right', '???', 'Middle']; button.attachEvent('onmousedown', handler); } 

jQuery fixes this IE flaw, it is worth checking event.which instead of magic with event.button
 $('button').mousedown(function (event) { alert(['Left', 'Middle', 'Right'][event.which]); }); 

Example: jsfiddle.net/azproduction/W2XgH
Read more: www.quirksmode.org/js/events_properties.html The paragraph “Which mouse button has been clicked?”
jQuery event.which: api.jquery.com/event.which

4. Is it possible to intercept keyboard key press events?

Is it possible to intercept keyboard key press events (down arrow, up) in javascript, so that the browser does not scroll the window after that? Are there any features among browsers in this behavior, if at all possible? For example, there is a table that does not fit onto the screen entirely, while moving through the rows is implemented using the arrow keys. It is necessary that the browser does not flip through this page.

To do this, you must cancel the so-called Default Action: the down arrow and mouse wheel scroll the window, the right mouse button brings up the context menu, by clicking on sumbit, form.submit() is form.submit() , when clicked on input, it will receive focus, when clicked the browser will click on the link, etc.

Using jQuery it can be done like this:
 //        keypress,     keyup $(window).bind($.browser.opera ? 'keypress' : 'keyup', function (event) { event.preventDefault(); //  return false; }); 

There is an important point. You must perform preventDefault() before defaultAction is executed. For example, when clicking on input, we do not want to transfer focus to it, then we need to hang the handler on an event in the chain before defaultAction - mousedown is executed.
 $('input').bind('mousedown', function (event) { event.preventDefault(); //  return false; }); 

The chain of events itself is as follows:
1. mousedown
2. focus (before the focus will work blur on another object)
3. mouseup
4. click
If we hang an event on focus and below, then it will not work. defaultAction will work after mousedown.

5. How to solve the problem of stopping gif-animation when pressing ESC, if this key is locked?


See answer above. Some browsers stop pressing gif animation when pressing Esc, stop loading the page - this is their default action.
It is necessary to cancel the default action event.preventDefault() :
 $(document).bind($.browser.webkit ? 'keydown' : 'keypress', function (event) { //  Esc if ((event.which || event.keyCode) === 27) { event.preventDefault(); //  return false; } }); 

6. And what is the operator (), with which the closure created?


The parentheses allow the parser to understand what these brackets follow the function: a grouping or a function call operator.

If you do this:
 function () { // source }() 

In this case, we get a SyntaxError due to the lack of a function name (the declaration function must always have a name).

If add name:
 function foo() { // source }() 

In the second case, the name is set (foo), and in theory, the function declaration should go normally. However, we still have a syntax error, but already with regards to the grouping operator without an expression inside. Note, in this case, it is the grouping operator that follows the function declaration, not the function call parenthesis!

There are other ways to prevent ParseError - to put a function in a state expression, i.e. show the parser that this is Function Expression:
From TheShock :
 !function () { // source }(); +function () { // source }(); [function() { // source }()]; var a = function () { // source }(); 

It is used incl. in jQuery. Allows you to select all the code in one block with a local scope. This speeds up access to internal variables, allows for cluttering up the global namespace and, more importantly, is compressed better by minifiers. habrahabr.ru/blogs/jquery/118564


Based on dsCode Subtleties ECMA-262-3. Part 5. Functions. - Question "about brackets"
More to read: kangax.github.com/nfe

7. Shipment code in XHR


The server sends the response “alert ('Boom !!!');” to user actions in the ajax system. On the client, the received response is run through eval () and executed. What is the name of such data transfer? This is not JSON, not XML, not HTML.

There are no names for this. In fact, this is a very bad approach, just as bad as storing the PHP code in the database and eval it every time. In addition to the conditional non-secularity, such an architecture carries with it a strong coherence, and in the future, it will be difficult to remake something. It turns out a mess: the data + code + representation, in such a model, in order to redo something we will have to unravel this tangle, make changes, taking into account numerous links, confuse it back. I'm not talking about tearing off a piece of the functionality from such a mash ...
To simplify code support, it is necessary to separate parts of the system as much as possible and reduce the number of links (dependencies). In order to obtain weak connectivity (when a piece of an application can be torn off or replaced as painlessly as possible), events and, for example, the MVC application architecture are entered.

Read:
And again about MVC
Application of an event-driven model in a web application
Writing complex interfaces with Backbone.js

8. How to organize the queue of execution of commands with a delay without hanging the entire script?


JavaScript has one thread in which the code itself is executed, the DOM tree is redrawn, timers are working. Every time when you perform a chain of operations (cycles, heavy functions), the user interacts with the interface (if the chain is not heavy, the user does not notice the changes) is blocked. To prevent the blocking of UI, Threed introduced Web Workers threads in JavaScript.
If the use of workers is not possible, then it is necessary to optimize cycles and heavy functions. As Nicholas C. Zakas writes in his book OReilly High Performance JavaScript: the user will not notice lags if the UI Threed is blocked for 100 ms or less. Those. we can calculate 100 ms, then it is worth unblocking UI Threed so that the user does not notice the lags.

Here is the original code optimized for all processors from his book:
 function timedProcessArray(items, process, callback) { var todo = items.concat(); //create a clone of the original setTimeout(function () { var start = +new Date(); do { process(todo.shift()); } while (todo.length > 0 && (+new Date() - start < 50)); if (todo.length > 0){ setTimeout(arguments.callee, 25); } else { callback(items); } }, 25); } function saveDocument(id) { var tasks = [openDocument, writeText, closeDocument, updateUI]; timedProcessArray(tasks, [id], function(){ alert("Save completed!"); }); } 

The timedProcessArray function blocks the UI Threed for 25 ms, performing a chain of actions, then releases the UI Threed for 25 ms and so on.

Read:
Nicholas C. Zakas - OReilly High Performance JavaScript
Web Workers Commix
Calculated using Web Workers
WXHR: The good old XHR with taste

9. Is it possible to somehow find out that the user has finished resizing the window?


There is no such event, but you can find out whether the user resizes the window for some time, which roughly corresponds to onresizeend

Sketch code:
 var time = 0, timerId, TIME_ADMISSION = 100; // 0.1s function onresizeend () { console.log('onresizeend'); }; function resizeWatcher () { if (+new Date - time >= TIME_ADMISSION) { onresizeend(); if (timerId) { window.clearInterval(timerId); timerId = null; } } }; $(window).resize(function () { if (!timerId) { timerId = window.setInterval(resizeWatcher, 25); } time = +new Date; }); 

A live example: jsfiddle.net/azproduction/2Yt6T

10. How to open a new window using window.open (), and not a tab?


This behavior is browser specific. Opera always opens the tab (but it appears as a window), Safari always opens the window (Safari behavior can be bypassed). Chrome, FF and IE are controllable.

If you pass additional parameters - the position of the window, a new window will open:
 window.open('http://www.google.com', '_blank', 'toolbar=0,location=0,menubar=0'); 

If nothing is transmitted, then the tab will open:
 window.open('http://www.google.com'); 

Often you need to open a new tab, there may be a problem with the safari: by default (depending on the settings) the safari with any call to the window.open function opens a new window. But when you click on a link with Ctrl+Shift/Meta+Shift always opens a new tab (regardless of the settings). To open a new tab, we will emulate the “click” event with Ctrl+Shift/Meta+Shift :
 function safariOpenWindowInNewTab(href) { var event = document.createEvent('MouseEvents'), mac = (navigator.userAgent.indexOf('Macintosh') >= 0); //  Ctrl+Shift+LeftClick/Meta+Shift+LeftClick () //    event.initMouseEvent( /* type */ "click", /* canBubble */ true, /* cancelable */ true, /* view */ window, /* detail */ 0, /* screenX, screenY, clientX, clientY */ 0, 0, 0, 0, /* ctrlKey */ !mac, /* altKey */ false, /* shiftKey */ true, /* metaKey */ mac, /* button */ 0, /* relatedTarget */ null ); //           -    $('<a/>', {'href': href, 'target': '_blank'})[0].dispatchEvent(event); } 

11. How to make a deep copy effectively?


If oldObject will not change, then it will be more efficient to clone through the prototype (well, very quickly):
 function object(o) { function F() {} F.prototype = o; return new F(); } var newObject = object(oldObject); 

If you need honest cloning, then it will be faster to walk recursively through the object tree + do some optimizations (this is by far the fastest algorithm for honest cloning):
 var cloner = { _clone: function _clone(obj) { if (obj instanceof Array) { var out = []; for (var i = 0, len = obj.length; i < len; i++) { var value = obj[i]; out[i] = (value !== null && typeof value === "object") ? _clone(value) : value; } } else { var out = {}; for (var key in obj) { if (obj.hasOwnProperty(key)) { var value = obj[key]; out[key] = (value !== null && typeof value === "object") ? _clone(value) : value; } } } return out; }, clone: function(it) { return this._clone({ it: it }).it; } }; var newObject = cloner.clone(oldObject); 


For jQuery, you can use the following:
 //   var newObject = jQuery.extend({}, oldObject); //   var newObject = jQuery.extend(true, {}, oldObject); 


Read:
Fair Clone Benchmark
Very long discussion of sabzh on stackoverflow

12. How to make an analog destructor / finalizer? And in general, how to manage the lifetime of objects?


In JavaScript, the object will be deleted when the last link to it disappears:

 var a = {z: 'z'}; var b = a; var c = a; delete az; delete a; //       "" console.log(b, c); //    : Object {} Object {},    

Those. Using the "destructor", you can not completely remove the object - you can only clear the contents.

13. Is it possible to do the processing of binary data? If so, how?


In JavaScript, all numbers are presented for use in a string view and there are no built-in tools for working with binary data. There is the BinaryParser JavaScript library for working with binary numbers: encoding, decoding (its code is hell!)

In ECMAScript 6+ (strawman) there is a StructType draft (this is a struct familiar to us from C ++ and others). It is needed to simplify working with binary files. Here is how it might look in the future:
 const Point2D = new StructType({ x: uint32, y: uint32 }); const Color = new StructType({ r: uint8, g: uint8, b: uint8 }); const Pixel = new StructType({ point: Point2D, color: Color }); const Triangle = new ArrayType(Pixel, 3); let t = new Triangle([{ point: { x: 0, y: 0 }, color: { r: 255, g: 255, b: 255 } }, { point: { x: 5, y: 5 }, color: { r: 128, g: 0, b: 0 } }, { point: { x: 10, y: 0 }, color: { r: 0, g: 0, b: 128 } }]); 


donnerjack13589 ArtemS For reading from buffers it is possible to use JavaScript typed arrays , but it will not work to get the number in binary form.

XMLHttpRequest Level 2 allows you to send and receive binary files:

14. How to change context variables of another function from one function?


1. You can give a reference to the primer context object in smth
2. Pass the function generated in the primer context to the smth function
 var primer = function (){ var a, b, c, d, e = {}; smth(function () { a = 1; b = 2; c = 3; d = 4; }, e); alert([a, b, c, d, e.pewpew]); }, smth = function (callback, e) { callback(); e.pewpew = "pewpew"; }; primer(); 


3. Previously (FireFox 3.6-) it was possible to reach the context through __parent__, but in version 4 we saw this feature out.

15. Regarding the article "Five ways to call a function." Which of these N ways (in heading 5, in article 4, a few in the comments) when to use better and why?


I will not consider the global call / method call and constructor, their scope is clear.
I will separately stop on call and apply. They do the same thing - they call the function with the explicit context this.
1. Call and apply for the designer override:
 //   function extend(newObj, oldObj) {function F() {}F.prototype = oldObj.prototype;newObj.prototype = new F();return newObj} var Obj = function () { this.obj_var = 100; }; Obj.prototype.obj_proto_var = 101; var NewObj = function () { Obj.call(this); //   Obj   Own property obj_var this.new_obj_var = 102; }; extend(NewObj, Obj) NewObj.prototype.new_obj_proto_var = 103; new NewObj(); // {new_obj_proto_var: 103, new_obj_var: 102, obj_proto_var: 101, obj_var: 100} 

2. Transformation of arguments NodeList and other array-like objects to an array, transformation of a live list (getElementsByTagName) to an array
 // document.getElementsByTagName("div")    ( ),        document.getElementsByTagName("div").forEach(function (elem) { // ... }); // TypeError: document.getElementsByTagName("div").forEach is not a function //   :    slice this,     Array.prototype.slice.call(document.getElementsByTagName("div")).forEach(function (elem) { // OK }); //      Array.prototype.slice.call('pewpew') // ["p", "e", "w", "p", "e", "w"] //  8-     undefined 

3. Tricks with Function.call.apply for creating wrappers:
We need to write a wrapper foo () that calls bar () in the specified context with an arbitrary number of arguments.
From hyborg
In a traditional way, it would look like this:
 function bar() {} // foo(context, arg1, arg2, ...) function foo() { var context = arguments[0]; var args = Array.prototype.slice.call(arguments, 1); //    bar bar.apply(context, args); } 

Salad use trick with call.apply instead:
 function foo() { Function.call.apply(bar, arguments); } 

It works like this: aplly calls Function.call on the bar object with the parameters passed to foo. That is, we get the following for the very first example with context and arg1, arg2:
 bar.call(context, arg1, arg2) 

4. Bind emulation

16. How to transfer the scope of execution of one function to another?


No Previously (FireFox 3.6-) it was possible to reach the context through __parent__, but in version 4 we saw this feature out.

17. How correctly to receive global object without its direct instructions, without eval and at 'use strict'?


No, if you omit one of the conditions, or execute only in the global scope, you can:
 // 1: eval - on (function(){ "use strict"; var globalObject = (0, eval)("this"); //  :) return globalObject; }()); // 2:   - on (function(global){ // ... }(window)); // 3: "use strict" - off (function(){ return this; }()); // 4:       ,       ,        . //     "use strict"; (function(global){ // global })(this); 

18. Is it possible to restart it in javascript after intercepting an event?


event takes no load; it is just an event descriptor. But you can explicitly pass a reference to the event handler, like this:
 $('#smth').click(function onSmthClick(event){ if (smth) { //   event.handlerFunction = onSmthClick; event.handlerContext = this; //    //  otherObjectSetSomeEvent   event.handlerFunction    otherObjectSetSomeEvent(event); } else { //  -  } }); 

But this is not a good solution, because will have a lot of extra stuff to wind. And the logic is very confusing.
It is better to redo the logic and divide the general handler into 2 parts:
 $('#smth').click(function handler1(event) { if (smth) { //    leftObjectSetSomeEvent(event, function handler2(e) { //  -  event  e }); } else { //  -  } }); function leftObjectSetSomeEvent(event, callback) { callback(event); //  -  event } 

19. And you did not think to write your js directory? Tell me where to learn JavaScript in depth? Books, tutorials?


There are many websites and many books , some of them have been translated into Russian .

20. How on JS to intercept all clicks on the page for any elements? That is, make a single click handler.


It is necessary to hang up the click event handler on the lowest object in the DOM tree, all clicks on the elements will “pop up” (if the traffic cops are not slowed down on the way forbid ascent) before it.
 // jQuery $(window).bind('click', function (e) { console.log('Clicked on ', e.target); }); //    - ,  jQuery delegate $('#pewpew').delegate('*', 'click', function (e) { console.log('Clicked on ', e.target); }); //     $('#pewpew').delegate('.pewpew', 'click', function (e) { console.log('Clicked on element with .pewpew class name'); }); 

21. How to run XHR without jQuery?


Non-cross browser feature:
 function xhr(m,u,c,x){with(new XMLHttpRequest)onreadystatechange=function(x){readyState^4||c(x.target)},open(m,u),send()} 

Cross browser, a bit longer:
 function xhr(m,u,c,x){with(new(this.XMLHttpRequest||ActiveXObject)("Microsoft.XMLHTTP"))onreadystatechange=function(x){readyState^4||c(x)},open(m,u),send()} 

Using:
 xhr('get', '//ya.ru/favicon.ico', function(xhr){console.dir(xhr)}); 

22. Work with file system


I wanted to learn about working with the file system through JavaScript, for example, to read and write to files. Most textbooks describe JavaScript for browsers, where working with files is not needed, but for example in a server application or, for example, XUL is a necessity, but there is very little documentation, if at all.

About client work with the file system, including upload files on the habr there are a lot of articles:
HTML5 File API: multiple files upload to server
Uploading files with the html5 File API, with preference and dancers
FileSystem API & File API: we understand and use
The shortest uploader pictures!

There is a good article about working with files on the Node.js server. Reading and writing files to Node.js

For XUL you can read here in the article MDC File_I / O

23. Reflow, repaint and methods to minimize them


1. If the browser supports the requestAnimationFrame function, then you should use it instead of setInterval/setTimeout
Browsers can optimize the animations going simultaneously, reducing the number of reflow and repaint to one, which in turn will lead to an increase in the accuracy of the animation. For example, animations on JavaScript synchronized with CSS transitions or SVG SMIL. Plus, if you are animating in a tab that is invisible, browsers will not continue redrawing, which will lead to less CPU, GPU, memory usage and as a result will reduce battery consumption in mobile devices.
2. Avoid a large number of float elements (decrease reflow)
4. Modify the DOM tree as rarely as possible - write to memory, and then insert it into the DOM 1 time (reflow will decrease)
5. Change the properties of an object with a bundle (decrease reflow, redraw) (this is not true for modern browsers)
 //  element.style.left="150px;"; //... element.style.color="green"; //    element.setAttribute('style', 'color:green;left:150px'); 

6. Spend animations with absolutely positioned objects only (reflow will decrease)
7. Before changing the group of elements, hide them style.display = "none" (decreases reflow) (this is not true for modern browsers)

Not on the topic, but also about optimization:
8. Use event delegation to reduce their number.
9. Cache references to DOM elements (selector call is the most expensive operation)
10. Use the quick selector functions of querySelectorAll () firstElementChild
11. Remember that document.getElementsByTagName returns a “live” collection of elements (if an element is added to the DOM tree, the collection will receive it automatically)

In many modern browsers, these methods will not give such a visible advantage (browser developers are all optimizing for us).

Read:
Nicholas C. Zakas - OReilly High Performance JavaScript
Advanced animations with requestAnimationFrame

24. Should I use childProcesses in node.js for each request in high-load projects?


In no case should you use childProcesses for each request because we get too much overhead (it's like PHP with apache): allocating extra memory, fork time, initialization time (jid compilation), CPU load, etc.Node.js distributes the load very well and loads one processor core in its “evented processing loop” - the main application thread. Ideal downloads for Node.js - 1 fork per core, best forked using Cluster . The cluster will act as a balancer (masters), and forks - slaves. Use childProcesses for heavy requests is justified.
You can also read here: stackoverflow.com/questions/3491811/node-js-and-cpu-intensive-requests

25. Using runInNewContext in node.js


What is runInNewContext? - node-js.ru/12-control-context-using-runinnewcontext
I see the only use of this technology for launching someone else's, potentially dangerous code (this is how Node.js hosting nodes). If there is no critical need for this, then I am categorically against it - this is an absolutely unnecessary wrapper, which you can not use if you choose the right application architecture and use agreements during development. What is bad: the creation / deletion of context - memory allocation as a consequence of frequent GC (which blocks the entire application). I think there will be problems with the support of such code.

Conclusion


TheShock will answer all the questions that are not in this article . For me another article about the architecture of heavy interfaces (gmail, and others).

If something is not clear - ask questions.

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


All Articles