Tips and Tricks that you may need to know when you work in JavaScript(Q&A)

A few days ago, we at SmartSpate have created a topic in which we collected 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 building “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”.

Prototype delegating inheritance

Classical inheritance is very similar to how people inherit the genes of their ancestors. There are some basic features: people can walk, talk … And there are characteristics for each person. People are not able to change themselves — their class (but they can change their own properties) and grandmothers, grandfathers, moms and dads cannot dynamically influence the genes of children and grandchildren. Everything is very terrestrial.

  • Now let’s imagine another planet on which there is no such gene inheritance as on Earth. There are mutants with “telepathic inheritance” who are able to change the genes of their descendants.

Let’s look at an example. The Father inherits the genes from Grandfather, and the Son inherits the genes from the Father, who inherits from Grandfather. Each mutant can freely mutate and can change the genes of its descendants. For example, Grandpa had green skin color, Father inherited color, Son also inherited color. Suddenly, the Grandfather decided: “I got tired of walking green — I want to be taken off,” he embarrassed (changed the prototype of his class) and “telepathically” distributed this mutation to the Father and the Son, in general everything turned blue. Then Father thought: “Grandfather in his old age moved at all” and changed his color in the genes back to green (changed the prototype of his class), and distributed his color “telepathically” to his son. Father and Son are green, grandfather is blue. Now, no matter how much the grandfather tried, the Father and the son did not change the color, because the Father now prescribed the color in his prototype, and the Son will first of all inherit from the Father’s Prototype. Now the Son decides: “Let me change my color to black and let my offspring inherit color from the Father” and prescribe my own property, which does not affect the offspring. And so on.

We describe everything in the code:

var Grandfather = function () {}; // Grandfather Constructor
Grandfather.prototype.color = 'green';
var Father = function () {}; // Constructor Father
Father.prototype = new Grandfather (); // This is a simple but not the best version of protypical inheritance
var Son = function () {}; // Son Constructor
Son.prototype = new Father (); // Similarly
var u = new Grandfather (); // Grandfather class instance
var f = new Father (); // Instance "class" Father
var s = new Son (); // Son instance
// Initially all green
console.log ([u.color, f.color, s.color]); // ["green", "green", "green"]
// Grandfather decided to change his color and color of offspring
Grandfather.prototype.color = 'blue';
console.log ([u.color, f.color, s.color]); // ["blue", "blue", "blue"]
// Father decided to return everything for himself and his posterity
Father.prototype.color = 'green';
// Although he could do it like this:
// Grandfather.prototype.color = 'green';
console.log ([u.color, f.color, s.color]); // ["blue", "green", "green"]
// There's no point
Grandfather.prototype.color = 'blue';
console.log ([u.color, f.color, s.color]); // ["blue", "green", "green"]
// The son decided not to take an example from Grandfather and changed only his own property
s.color = 'black'; // Change our own property that does not affect the prototype chain
console.log ([u.color, f.color, s.color]); // ["blue", "green", "black"]
var SonsSon = function () {}; // Constructor SonsSon
SonsSon.prototype = new Son (); // Similarly
var ss = new SonsSon (); // SonsSon "class" instance
// Son of the son inherited from the Father
console.log ([u.color, f.color, s.color, ss.color]); // ["blue", "green", "black", "green"]

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, but compared to 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 do I encourage an error with new — some check if this is global, then the user has called the constructor without new and creates an object inside the constructor and returns it — this is an error promotion and ideologically incorrect approach.

var Obj = function () {
"use strict";
this.pew = 100;
};
// Right
new Obj.pew ++;
// user catches an error
Obj (); // TypeError: this is undefined

The new is not acceptable for factory methods, and short constructors — jQuery

I summarize the code:

// Good: less code, error is not encouraged, use strict
var Obj = function () {
"use strict";
this.pew = 100;
};
// Not very good: superfluous and completely unnecessary code
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 mouse-down mouse-up event is triggered by all mouse buttons, the click is just the left. 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]);
});

4. Is it possible to intercept keyboard keypress 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 raises the context menu, by clicking on submit, form.submit () is executed, when clicked on input, it gets focus the browser will click on the link, etc.

Using jQuery it can be done like this:

// IE and safari doesn't track arrows on keypress, but Opera is buggy on keyup
$ (window) .bind ($. browser.opera? 'keypress': 'keyup', function (event) {
event.preventDefault ();
// or
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 ();
// or
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 the 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, when pressing Esc, stop the gif animation, stop loading the page — this is their default action.
It is necessary to cancel the default action of the event.preventDefault ():

$ (document) .bind ($. browser.webkit? 'keydown': 'keypress', function (event) {
// Press Esc
if ((event.which || event.keyCode) === 27) {
event.preventDefault ();
// or
return false;
}
});

6. What is the operator () with which the closure was created?

The parentheses allow the parser to understand what these brackets follow the function: the group or the 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 with no 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 SmartSpate:

!function () {
// source
}();
+function () {
// source
}();
[function() {
// source
}()];
var a = function () {
// source
}();
It is used including 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 minifier.

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 this data transfer called? 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. Apart from the conditional non-secularity, such an architecture carries with it a strong coherence, and in the future, something will be difficult to remake. It turns out a mess: 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 is entered.

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 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 UI blocking, 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 UI Threed for 25 ms, performing a chain of actions, then releases UI Threed for 25 ms and so on.

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;
});

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 pressed, it 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 pressed:

function safariOpenWindowInNewTab (href) {
var event = document.createEvent ('MouseEvents'),
mac = (navigator.userAgent.indexOf ('Macintosh')> = 0);
// do Ctrl + Shift + LeftClick / Meta + Shift + LeftClick (focus)
// create your own event
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
);
// create a link in memory and click this event by reference - a new tab will open
$ ('<a/>', {'href': href, 'target': '_blank'}) [0] .dispatchEvent (event);
}

11. How to make a deep copy effectively?

If oldObject does 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:

// Shallow Copy
var newObject = jQuery.extend ({}, oldObject);
// deep copying
var newObject = jQuery.extend (true, {}, oldObject);

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 a.z;
delete a; // We just delete kill the link "a"
console.log (b, c); // The object actually exists: Object {} Object {}, but it is empty

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 } }]);

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 methods (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 stop separately on call and apply. They do the same thing — they call the function with the explicit context this.
1. Сall and apply for the designer override:

// Auxiliary function
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); // Call the Obj constructor and get the 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") does not return an array (although it is similar), so we cannot execute array methods
document.getElementsByTagName ("div"). forEach (function (elem) {
// ...
}); // TypeError: document.getElementsByTagName ("div"). ForEach is not a function
// Lead to an array: we slip the fuction function slice this, which is a trip to the array
Array.prototype.slice.call (document.getElementsByTagName ("div")). ForEach (function (elem) {
// OK
});
// Similarly, you can do with strings
Array.prototype.slice.call ('pewpew') // ["p", "e", "w", "p", "e", "w"]
// In IE8, there will be an array from 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.

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); // make an array of arguments for bar
bar.apply (context, args);
}
Instead of this salad use the trick with call.apply:
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. 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.

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"); // Magic :)
return globalObject;
} ());
// 2: specify the name - on
(function (global) {
// ...
} (window));
// 3: "use strict" - off
(function () {
return this;
} ());
// 4: If we execute this code in globals, we will get a reference to a global variable, but otherwise it will not work.
// This is the best option
"use strict";
(function (global) {
// global
}) (this);

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

The event does not carry any 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) {
// Write handler
event.handlerFunction = onSmthClick;
event.handlerContext = this;
// pass to the left object
// now otherObjectSetSomeEvent can use event.handlerFunction and call the handler
otherObjectSetSomeEvent (event);
} else {
// do something else
}
});

But this is not a good solution, because will have a lot of extra stuff to the wind. And the logic is very confusing.
It is better to alter the logic and divide the general handler into 2 parts:

$ ('# smth'). click (function handler1 (event) {
if (smth) {
// pass to the left object
leftObjectSetSomeEvent (event, function handler2 (e) {
// do something with event or e
});
} else {
// do something else
}
});
function leftObjectSetSomeEvent (event, callback) {
callback (event);
// do something with the event
}

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

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, by the way, prohibiting ascent) before it.

// jQuery
$ (window) .bind ('click', function (e) {
console.log ('Clicked on', e.target);
});
// You can also limit it to some area using jQuery delegate
$ ('# pewpew'). delegate ('*', 'click', function (e) {
console.log ('Clicked on', e.target);
});
// You can limit goals
$ ('# pewpew'). delegate ('. pewpew', 'click', function (e) {
console.log ('Clicked on element with .pewpew class name');
});

20. 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 slightly 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', '//google.com/favicon.ico', function(xhr){console.dir(xhr)});

21. 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 reflows 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 the animation is performed in a tab that is invisible, browsers will not continue to redraw, 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-in 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)

// Instead
element.style.left = "150px;";
// ...
element.style.color = "green";
// Change all at once
element.setAttribute ('style', 'color: green; left: 150px');

6. Drag 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).

22. 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 “event processing loop” — the main application thread. Ideal download 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.

25. Using runInNewContext in node.js

What is runInNewContext?

I see the only application of this technology for launching someone else’s, potentially dangerous code (this is how Noter.js hosting nodester does). 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.