
Javascript quality guide
With the help of the tips suggested in this guide, you will write code that is understandable to any development team.
From translator
Hello everyone, Maxim Ivanov with you, and today we will talk about the
rules for formatting the code in JavaScript .
Nicolas Bevacqua , author of JavaScript Application Design, developer from Argentina, published this guide a long time ago, the first entry appeared in 2014, much is written according to the ES5 standard, however, these days it is all the same true now, when ES6 is still nowhere fully working without babel and other transpilers. Although we are seeing progress in top desktop browsers (Google Crhome, Firefox), where 70-90% of the plans have already been implemented, we see that they are striving to maintain the new standard, but unfortunately, there are
still no browsers that could fully support ES6 . By the way, I will be very glad to your comments. In general, good luck and let's get started.
Introduction
These recommendations are not carved in stone, these are basic rules that will help you learn how to write more consistent and understandable code. To effectively apply the proposed rules in practice, start right now, share them with your colleagues and introduce them into production. However, do not dwell on these rules, it can be fruitless and counterproductive. Try to find a middle ground to make everyone comfortable in your team. Automated checking of code style is quite a useful thing, since the style of writing code is everyone’s business.
')
Content
- Modules
- Strict regime
- Formatting spaces
- Semicolon
- Code style
- Error code analysis
- Strings
- Variable initialization
- Conditional constructions
- Comparisons
- Ternary operator
- Functions
- Prototypes and Inheritance
- Objects
- Arrays
- Regular expressions
- Developer Console
- Comments
- Naming
- Polyfill library
- Daily Tricks
- ES6 Guide
Modules
This item assumes that you are using modular systems such as CommonJS, AMD, ES6 Modules, or any other. Modular systems work with a separate scope, without affecting global objects, they also provide a more organized code structure by automatically generating dependencies, freeing you from inserting the
<script> tag yourself.
Modular systems can load templates, which in turn are useful for local solutions when it comes to testing a separate isolated component.
To read :
1.
What is a module?2.
CommonJS Modules3.
Modules in JSStrict regime
Always use
'use strict'; at the top of your module. Strict mode allows you to catch the errors of the language, although it is a bit scary name, this mode allows the interpreter to monitor the quality of your code, thus you spend less time correcting errors.
To read
1.
Use strict directive2.
Strict ModeFormatting spaces
Control the number of spaces when formatting a code. For this, it is recommended to use the
.editorconfig configuration
file . I suggest using these settings.
Using tabs and spaces is your own business, but I recommend two spaces to indent. The
.editorconfig file
takes care of everything, the formatting process will also be monitored when you press the Tab key.
You can customize not only the correct tab, you can also adjust the indentation in the right places, for example, before, after or between something. True to perform control over such things is quite a laborious task.
function () {} function( a, b ){} function(a, b) {} function (a,b) {}
Try to minimize the differences in the formatting of your code.
Where possible, improve the readability of the code, try to write a maximum of 80 characters in one line.
To read:
1.
EditorConfig2.
One settings for all editors3.
Install EditorConfig in Sublime text 34.
80-characters5.
Good programming styleSemicolon
Most JavaScript programmers prefer to
use a semicolon . The semicolon must always be set in order to avoid automatic substitution (ASI). There are languages in which the semicolon is optional, and nobody puts it there. In JavaScript, the newline replaces it, but only partially, so it’s better to put it if you understand the
ASI rules.
Regardless of the fact that you are sure of your code, use the validator (linter) to catch unnecessary semicolons.
To read:
1.
JavaScript Semicolon Insertion2.
Code Style Tips3.
Improving the quality of javascript code4.
Everything you need to know about the semicolon5.
An open letter to JS leaders regarding semicolonsCode style
You should not bother with this, because this is an extremely painful and tense moment, which does not bring any noticeable success if you follow the same strict policy in other areas.
Of course, a certain code design standard (programming style) (English coding convention) exists. In the JavaScript world, there are even tools to check your code for compliance with this standard - JSCS (JavaScript Code Style). Having a common programming style makes it easier to understand and maintain source code written by more than one programmer, and also simplifies the interaction of several people when developing software.
These standards come as programmers and the best teams, as well as entire companies:
1.
Airbnb2.
Douglas Crockford3.
Google4.
Grunt5.
Idiomatic6.
jQuery7.
MDCS8.
Node.js9.
Wikimedia10.
WordPress11.
YandexTo read:
1.
Programming style2.
JavaScript Code StyleError code analysis
On the other hand, validation is sometimes necessary. If you are not using a validator, then you are exactly sure how your code works, and you do not need to use, for example, JSLint (software quality control tool). And yet, I recommend using JSHint or ESLint.
A few tips in using JSHint:
- Declare the .jshintignore file in node_modules, bower_components and others
- Use the rules below in the .jshintrc file
{ "curly": true, "eqeqeq": true, "newcap": true, "noarg": true, "noempty": true, "nonew": true, "sub": true, "undef": true, "unused": true, "trailing": true, "boss": true, "eqnull": true, "strict": true, "immed": true, "expr": true, "latedef": "nofunc", "quotmark": "single", "indent": 2, "node": true }
Of course, you are not obliged to strictly adhere to these rules, but it is important to find a middle ground between the validator and other programming styles.
To read:
1.
JSLint2.
JSHint3.
A few tips on setting up ESlint4.
Configure JSHint in sublime text 35.
JavaScript linttingStrings
All strings must have the same type of quotes. Use single or double quotes throughout the code.
This will work faster if you use a formatting function similar to that of
util.format in Node.js. Thus, it will be easier for you to format the lines, and the code will look much cleaner.
You can implement something like this by using the code below.
function format () { var args = [].slice.call(arguments); var initial = args.shift(); function replacer (text, replacement) { return text.replace('%s', replacement); } return args.reduce(replacer, initial); }
To declare multiline (multiline), especially when it comes to HTML snippets, it is sometimes better to use an array as a data buffer, the elements of which can later be merged into a string. Of course, concatenating (merging) strings is much easier in the familiar style and, of course, it will work faster, but sometimes it is more difficult to track.
var html = [ '<div>', format('<span class="monster">%s</span>', name), '</div>' ].join('');
You can insert your snippets into the array to be merged and at the end merge them to better read the code. This is how some template engines work, such as Jade.
To read:
1.
Strings2.
Work with strings3.
Performance of string concatenation4.
C-like representation of strings based on typed arraysVariable initialization
Always declare variables in meaning and semantics, fixing them at the top of the scope. Initialization of variables on each line one by one is welcome. Of course, you can declare the
var statement once, and then specify variables using a comma, but be consistent across the whole project.
Anyway, you can see exactly where the specific variable is declared.
You can declare empty variables in one line separated by commas.
Conditional constructions
Use curly braces. In some cases, this will help you avoid a critical bug with Apple products while the SSL / TLS secure connection protocol is running.
For the sake of understanding the content, try to keep the contents of the condition block not on one line.
To read:
1.
Apple's SSL / TLS bug2.
Here it is failComparisons
Avoid using == and! = Operators, use more favorable comparison operators === and! ==. These operators are called hard comparison operators, while their alternatives (== and! =) Transform operands to the same data type.
Ternary operator
The ternary operator is clearly suitable for specific conditions and is completely unsuitable for multi-component ones. As a rule, if your trained eye cannot make out a condition in the ternary operator as quickly as your brain can quickly read the text and interpret it, for the sake of your good, do not use the ternary operator.
jQuery is a prime example that is filled with
unpleasant three-component operators .
In cases where the condition may be confusing, just use
if and
else .
Functions
When initializing a function, use a
functional declaration instead of
functional expressions . All this affects the
"lifting" of variables and the declaration of functions.
There is nothing wrong with functional expressions if you use them in
currying.Keep in mind that the functional declaration of the functions will also be available in the scope above, so all of this does not matter in what order this function was declared. As a rule, you should always declare functions in the global scope, and you should try to avoid placing such functions within conditional statements.
If you need an empty (
no-op ) method, you can either use an extension using the
Function.prototype prototype or use the
function noop () {} function . Also, ideally, you should have one reference to your own method, which is used throughout the application. Instead of writing the same type of code constructs, your method will be a template for similar constructs.
Binding can be done using the function .call () from the prototype of the Function.prototype function, the record can also be reduced to [] .slice.call (arguments) instead of using Array.prototype.slice.call (). In any case, it can be simplified by using the bind () function.
However, it should be remembered that there is a significant
performance degradation in the V8 engine when using this approach.
Do not declare functions inside loops.
Or even better, just use
.forEach , which is optimized for use cases in a loop.
In order to view the call stack much easier, you need to reduce the use of anonymous functions. That is, if you are used to assigning all your callbacks as anonymous functions, you can try assigning them names. This will determine the root cause of the exception when analyzing the stack trace.
Avoid nested conditions in functions; sometimes it is better to use an empty return value, thus your code will be more readable.
If the function returns nothing or returns an empty return, then it returns the value
undefined .
To read:
1.
Scope in JavaScript and "raising" variables and function declarations2.
Currying3.
Using the empty function, using jQuery as an example4.
Massive objects5.
stack trace6.
Tips for debugging javascript in asynchronous call stacks7.
JavaScript return in the function returns undefinedPrototypes and Inheritance
At all costs, avoid extending standard prototypes. If you need to extend the functionality of native
data types (structures) of a language, use the
poser library or develop your own implementation of the data structure.
Basic data structures:
Do not confuse primitive values with object values (true for String, Number, Boolean).
Avoid the prototype model of inheritance, because it can affect performance. One of the common mistakes in programming in JavaScript is just the extension of the basic prototypes. This technology, called monkey patching, violates the principle of encapsulation. Despite the fact that it is used in common frameworks such as Prototype.js, there are currently no reasonable reasons for its use, since in this case, the built-in types are cluttered with additional non-standard functionality. The only justification for extending the base prototypes is just emulating new features, such as Array.forEach, for old versions of the language that do not support them.
- Prototype inheritance forces you to use the this keyword always
- There is more abstraction than using simple objects.
- This is the cause of the headache when creating new objects.
- Try to use simple objects.
To read:
1.
JavaScript data types and data structures2.
Determination of data type3.
What is a prototype?4.
Prototype-oriented programming5.
Inheritance and prototype chain6.
What is this and
context definition in practiceObjects
To initialize an object, we can use curly braces
{} , which will be an object literal. Use the
factory creation approach instead of using a pure constructor to create an object.
Creating an object:
var World = {};
var World = { people: "~ 7 .", country: "~ 258 " }; console.log(World);
Creating an object using a custom prototype (approach in using the constructor):
Creating an object using a custom prototype (approach to the use of factories):
A good example of prototyping:
Copy by reference and pass by value
More clearly and transparently, how data is transferred between memory cells can be seen in C / C ++. But in JavaScript, it is very desirable to remember about the mechanism of operation of objects and then some questions will disappear immediately.
Variables
Objects
To read:
1.
Objects: transmission by referenceArrays
We can use square brackets
[] , which will be an array literal, to initialize the array. To increase performance, you can create an array using the
new Array ( length ) construct with its length (array size).
In order to properly manipulate the elements of an array, you need to know the standard methods. Everything is much simpler than you can imagine.
Standard methods:
More advanced methods:
Copy by reference
Let's start studying standard methods:
- Enumerating elements is done using .forEach
- Checks with .some and .every
- Merge with .join and .concat
- Working with the stack and queue with .pop, .push, .shift, and .unshift
- Enumeration method (mapping) .map
- Run queries .filter
- Sort with .sort
- Calculations with .reduce, .reduceRight
- Copying with .slice
- More powerful tool for removing and adding .splice elements
- Search for an element in an .indexOf array
- To help operator in
- Reverse a search or reversal of an array using .reverse

Enumerating elements is done using .forEach
This is one of the simplest JavaScript methods. Not supported in IE7 and IE8.
Accepts a callback function (callback), which is called once for each element in the array, and is passed through three main arguments:
- value - the value that contains the current element of the array
- index - the position of the element in the array
- array - is a link to an array
In addition, we can pass an optional second argument (object, array, variable, whatever), this argument will be available in the callback function and presented as a context (this).
['_', 't', 'a', 'n', 'i', 'f', ']'].forEach(function (value, index, array) { this.push(String.fromCharCode(value.charCodeAt() + index + 2)) }, out = []) out.join('')
We cheated, .join, we have not yet considered, but soon we get to this. In our case, it combines various elements in the array, this method works much more efficiently than doing it manually
out [0] + '' + out [1] + '' + out [2] + '' + out [n] . By the way, we cannot break the foreach-cycle, as it is done in ordinary searches using
break . Fortunately, we have other ways.
Checks with .some and .every
These methods are related to assert functions. If you have ever worked with enums in .NET, you’ll understand that they look like their cousins .Any (x => x.IsAwesome) and .All (x => x.IsAwesome).
These methods are similar to .forEach in that they also accept a callback function with the same arguments, but are slightly different in nature.
The some () method calls the passed callback function once for each element present in the array until it finds one for which the callback returns the true value (the value that becomes true when casting to Boolean type). If such an element is found, the some () method will immediately return true. Otherwise, if callback returns false for all elements of the array, the some () method returns false. The callback function is called only for array indices that have assigned values; it is not called for indexes that have been deleted or to which values have never been assigned.
max = -Infinity satisfied = [10, 12, 10, 8, 5, 23].some(function (value, index, array) { if (value > max) max = value return value < 10 }) console.log(max)
The .every () method works in the same way, but closures only happen when your callback function returns false, not the truth.
Merge with .join and .concat
The .join method is often confused with the .concat method, as you know, the method accepts a separator in the .join (separator) argument, it creates a string, as a result, it takes each element of the array and separates them with a separator in this string. If no separator is specified, the default is separator = ','.
The concat () method returns a new array consisting of the array on which it was called, connected to other arrays and / or values passed as arguments.- .concat works on this principle: array.concat (val, val2, val3, valn)
- .concat returns a new array
- array.concat () with no arguments returns an incomplete copy of the array
An incomplete copy means that the copy will contain the same object references as the original array.As a rule, both the original and the new array refer to the same object.That is, if the object by reference is changed, the changes will be visible both in the new and in the original arrays. var a = { foo: 'bar' } var b = [1, 2, 3, a] var c = b.concat() console.log(b === c)
Working with the stack and queue with .pop, .push, .shift, and .unshift
Nowadays, everyone knows that adding elements to the end of an array is done using .push. Can you immediately add multiple elements using the [] .push ('a', 'b', 'c', 'd', 'z') construct? var a = [].push('a', 'b', 'c', 'd', 'z'); console.log(a);
var a = []; a.push('a', 'b', 'c', 'd', 'z'); console.log(a);
The pop () method removes the last element from the array and returns its value. If the array is empty, returns undefined (void 0).Using .push and .pop we can develop our own LIFO stack structure. function Stack () { this._stack = [] } Stack.prototype.next = function () { return this._stack.pop() } Stack.prototype.add = function () { return this._stack.push.apply(this._stack, arguments) } stack = new Stack() stack.add(1, 2, 3) stack.next()
On the contrary, we can create a FIFO queue using .unshift and .shift. function Queue () { this._queue = [] } Queue.prototype.next = function () { return this._queue.shift() } Queue.prototype.add = function () { return this._queue.unshift.apply(this._queue, arguments) } queue = new Queue() queue.add(1,2,3) queue.next()
Using .shift (or .pop) in a loop for a set of array elements, we can clear it in the process. list = [1,2,3,4,5,6,7,8,9,10] while (item = list.shift()) { console.log(item) } list
Enumeration method (mapping) .map
map() .
map callback . callback , , undefined. ( , , ).
Array.prototype.map , .forEach, .some, .every:
.map(fn(value, index, array), thisArgument).
values = [void 0, null, false, ''] values[7] = 'text' result = values.map(function(value, index, array){ console.log(value) return value })
undefined × 3 — , .map , - . .map .
.filter
filter() , , .
filter() callback , , , callback true , true boolean. callback , ; , . , callback, .
: .filter(fn(value, index, array), thisArgument). , .filter , callback-, .
[void 0, null, false, '', 1].filter(function (value) { return value })
.sort
sort() . . c Unicode.
: Array.prototype.sort(fn(a,b)), , , :
- return value < 0, a b
- return value === 0,
- return value > 0, a b
[9,80,3,10,5,6].sort()
.reduce, .reduceRight
reduce() callback , , , : ( callback), , , .
, reduce() reduceRight() , , reduce() , reduceRight() .
: reduce(callback(previousValue, currentValue, index, array), initialValue).
previousValue currentValue . reduce() initialValue, previousValue initialValue, currentValue . initialValue , previousValue , currentValue .
.reduce :
Array.prototype.sum = function () { return this.reduce(function (partial, value) { return partial + value }, 0) }; [3,4,5,6,10].sum()
, . .join . , :
function concat (input) { return input.reduce(function (partial, value) { if (partial) { partial += ', ' } return partial + value.name }, '') } concat([ { name: 'George' }, { name: 'Sam' }, { name: 'Pear' } ])
.slice
.concat, .slice - . — . Array.prototype.slice .
Array.prototype.slice.call({ 0: 'a', 1: 'b', length: 2 })
.concat:
Array.prototype.concat.call({ 0: 'a', 1: 'b', length: 2 })
, .slice — :
function format (text, bold) { if (bold) { text = '<b>' + text + '</b>' } var values = Array.prototype.slice.call(arguments, 2) values.forEach(function (value) { text = text.replace('%s', value) }) return text } format('some%sthing%s %s', true, 'some', 'other', 'things')
.splice
splice() , / . , . , , .concat .slice.
var source = [1,2,3,8,8,8,8,8,9,10,11,12,13] var spliced = source.splice(3, 4, 4, 5, 6, 7) console.log(source)
It may have already been noted that it also returns deleted items and sometimes it can be useful. var source = [1,2,3,8,8,8,8,8,9,10,11,12,13] var spliced = source.splice(9) spliced.forEach(function (value) { console.log('removed', value) })
Search for an element in an .indexOf array
The indexOf () method compares the searchElement element with the elements in the array using a strict comparison (the same method is used by the === operator, the triple is equal). var a = { foo: 'bar' } var b = [a, 2] console.log(b.indexOf(1))
To help operator in
A common mistake of newbies at the interview is their confused opinion about the identical operation of the .indexOf method and the in operator. var a = [1, 2, 5] 1 in a
The fact is that the in operator checks the key of the object, and does not search by value. This option works much faster than using .indexOf: var a = [3, 7, 6] 1 in a === !!a[1]
Reverse a search or reversal of an array using .reverse
This method will take the elements in the array and place them in reverse order. var a = [1, 1, 7, 8] a.reverse()
To read:
1.
Array2.
Array vs. Object3.
JavaScript4.
5.
Objects/Arrays JavaScript6.
7.
, ,
/ / , . . , (inline), .
, ,
. ,
.
:
1.
2.
RegExp3.
C4.
RegExp String5.
JavaScript precompiled6.
7.
- console, . . , . , . - (console.log
:
1.
JavaScript2.
console3.
.4.
Chrome. Web Inspector5.
Chrome console APIComments
, . . , , , , . , . , .
, !
:
1.
162.
3.
4.
JavaScript-
. , , .
:
1.
JavaScript Style Guide and Coding ConventionsPolyfill-
, , , polyfill-, . , , .
polyfill-, , , polyfill.
:
1.
DOM:2.
10 polyfill-3.
polyfill-
1.
|| , . , (false || true). , - , false, 0, null '', , , . :
if (value === void 0) { value = defaultValue } function a (value) { var defaultValue = 33; var used = value || defaultValue; }
2.
, .bind
function sum (a, b) { return a + b; } var addSeven = sum.bind(null, 7); addSeven(6);
3.
Array.prototype.slice.call ,
var args = Array.prototype.slice.call(arguments);
4.
!
var emitter = contra.emitter();
5. (no-op), Function()
function (cb) { setTimeout(cb || Function(), 2000); }
:
1.
browserhacks2.
5 JavaScript-3.
JavaScript4.
5.
JavaScript: argumentsES6
JavaScript ECMA ( W3C), ECMAScript. ECMAScript :
- — , , ,
- — , ,
- — JSON, Math, ,
2009 ES5 ( ES5.1 2011), , Firefox, Chrome, Opera, Safari . JS ( 2013, 2014, 2015) — ES6.
ES6 — . , JS (ES5), ES6 , . ! ES6 . . . JS, -. - , ( ).
:
1.
Style Guide ES62.
ES63.
ES64.
ES6 :5.
ES66.
ECMAScript 67.
350 ES6