Sunteți pe pagina 1din 16

JavaScript Best Practices

1) Reference material and recommendations


Best JavaScript Style Guide. https://github.com/airbnb/javascript. this is the one we are following for
the Evolve Project.
we can use the Airbnb styleguide, and adapt some rules to our needs:
https://www.npmjs.com/package/eslint-config-airbnb [React projects]
https://www.npmjs.com/package/eslint-config-airbnb-base [Non-React projects]

These are our rules respect to Airbnb Style guide:


"rules": {
"import/no-extraneous-dependencies": "off",
"no-underscore-dangle": "off",
"global-require": "off",

"valid-jsdoc": [2, {"requireReturn": false}],

"react/prefer-stateless-function": "off",
"react/require-default-props": "off",
"react/forbid-prop-types": "off"
}

We recommend to use ESLint as JavaScript linter over any other one. Its the current to go forward.
http://www.npmtrends.com/jshint-vs-eslint-vs-jslint-vs-babel-eslint-vs-jscs
https://www.sitepoint.com/comparison-javascript-linting-tools/

Compatibility guide for ES5, ES6 and ES7 for different browsers and mobile OS
http://kangax.github.io/compat-table/es5/

Whats new in ES6


http://es6-features.org/#Constants

Free online book on ES6 and ES7


http://exploringjs.com/es2016-es2017/

2) Choose easy to understand and short names for variables and functions.
This is a no-brainer but it is scary how often you will come across variables like x1, fe2 or xbqne in
JavaScript, or on the other end of the spectrum variable names
like incrementorForMainLoopWhichSpansFromTenToTwenty or createNewMemberIfAg
eOverTwentyOneAndMoonIsFull.
None of these make much sense good variable and function names should be easy to understand
and tell you what is going on not more and not less. One trap to avoid is marrying values and
functionality in names. A function called isLegalDrinkingAge()makes more sense
than isOverEighteen() as the legal drinking age varies from country to country, and there are
other things than drinking to consider that are limited by age.
See your code as a narrative. If you can read line by line and understand what is going on, well done.
If you need to use a sketchpad to keep up with the flow of logic, then your code needs some work.

3) Declare and Initialize Variables


Always use const and let instead var
let and var are different, so if you just do a global search-and-replace throughout your code, that
could break parts of your code that (probably unintentionally) depend on the quirks of var. But for
the most part, in new ES6 code, you should just stop using var and use let everywhere instead.
Hence the slogan: let is the new var.

Variables declared with const are just like let except that you cant assign to them, except at the
point where theyre declared.
It is a good coding practice to initialize variables when you declare them. This will:
Give cleaner code
Provide a single place to initialize variables
Avoid undefined values

let firstName = "";


const MAX_CAT_SIZE_KG = 3000;

Note: let and const are block scoped. Therefore, referencing block-scoped identifiers before they
are defined will produce a ReferenceError. Leave var declarations inside of legacy code to denote that
it needs to be carefully refactored. When working on a new codebase, use let for variables that will
change their value over time, and const for variables which cannot be reassigned.

4) Include All Necessary Semicolons


Most developers won't intentionally fail to put semicolons in the proper places. However, you need to
be aware that the browser will usually put in semicolons where it thinks they are necessary. This can
enable a bad habit that will come back to bite you down the road. In some instances, the compiler
might assume that a semicolon is not needed, which will introduce tricky, hard-to-find bugs into your
code. Avoid this by always adding the proper semicolons. A good tool to help you check your JavaScript
for forgotten semicolons is http://eslint.org/demo/.

5) Dont use delete to remove an item from array


Use splice instead of using delete to delete an item from an array. Using delete replaces the item
with undefined instead of the removing it from the array.

6) Use Slice to select items from Array


The filter method is so clean, but it means that weve got to loop through every item in the array at
least once. That seems like overkill
7) Never Declare Number, String, or Boolean Objects

Always treat numbers, strings, or Boolean as primitive values. Not as objects. Declaring these types as
objects, slows down execution speed, and produces nasty side effects:

let x = "John";
let y = new String("John");
(x === y) // is false because x is a string and y is an object.

let x = new String("John");


let y = new String("John");
(x == y) // is false because you cannot compare objects.

8) Don't Use new Object()

Use {} instead of new Object()


Use "" instead of new String()
Use 0 instead of new Number()
Use false instead of new Boolean()
Use [] instead of new Array()
Use /()/ instead of new RegExp()
Use function (){} instead of new Function()

let x1 = {}; // new object


let x2 = ""; // new primitive string
let x3 = 0; // new primitive number
let x4 = false; // new primitive boolean
let x5 = []; // new array object
let x6 = /()/; // new regexp object
let x7 = function(){}; // new function object

9) Beware of Automatic Type Conversions

Beware that numbers can accidentally be converted to strings or NaN (Not a Number). JavaScript is
loosely typed. A variable can contain different data types, and a variable can change its data type:
let x = "Hello"; // typeof x is a string
x = 5; // changes typeof x to a number

When doing mathematical operations, JavaScript can convert numbers to strings:


let x = 5 + 7; // x.valueOf() is 12, typeof x is a number
let x = 5 + "7"; // x.valueOf() is 57, typeof x is a string
let x = "5" + 7; // x.valueOf() is 57, typeof x is a string
let x = 5 - 7; // x.valueOf() is -2, typeof x is a number
let x = 5 - "7"; // x.valueOf() is -2, typeof x is a number
let x = "5" - 7; // x.valueOf() is -2, typeof x is a number
let x = 5 - "x"; // x.valueOf() is NaN, typeof x is a number

10) Use Decimals Cautiously


When is 0.1 + 0.2 not equal to 0.3? When you do the calculation in JavaScript. The actual value of 0.1
+ 0.2 comes out to be something like 0.30000000000000004. The reason for this (nope, not a bug) is
because JavaScript uses Binary Floating Point numbers. To get around this issue, you can multiply your
numbers to remove the decimal portion. For instance, if you were to be adding up the cost of two
items, you could multiply each price by 100 and then divide the sum by 100. Here is an example:
let hamburger = 8.20;
let fries = 2.10;
let total = hamburger + fries;
console.log(total); //Outputs 10.299999999999999

hamburger = hamburger * 100;


fries = fries * 100;
total = hamburger + fries;
total = total / 100;
console.log(total); //Outputs 10.3

11) Use === Comparison


The == comparison operator always converts (to matching types) before comparison. The === operator
forces comparison of values and type:

0 == ""; // true
1 == "1"; // true
1 == true; // true

0 === ""; // false


1 === "1"; // false
1 === true; // false

12) Use Parameter Defaults


If a function is called with a missing argument, the value of the missing argument is set to undefined.
Undefined values can break your code. It is a good habit to assign default values to arguments.

function myFunction(x, y = 0) {}

13) End Your Switches with Defaults


Always end your switch statements with a default. Even if you think there is no need for it.
switch (new Date().getDay()) {
case 0:
day = "Sunday";
break;
case 1:
day = "Monday";
break;
case 2:
day = "Tuesday";
break;
case 3:
day = "Wednesday";
break;
case 4:
day = "Thursday";
break;
case 5:
day = "Friday";
break;
case 6:
day = "Saturday";
break;
default:
day = "Unknown";
}

14) Avoid Using eval()


The eval() function is used to run text as code. In almost all cases, it should not be necessary to use it.
Because it allows arbitrary code to be run, it also represents a security problem.

15) Pass functions, not strings, to setTimeout() and setInterval()


If you pass a string into setTimeout() or setInterval(), the string will be evaluated the same way as
with eval, which is slow.

16) Avoid Globals


Note: ES6 helps you with modules (each file has its own scope) and const and let (no hoisting and no
global variables). If you are using ES5 in current legacy applications follow this rule.
Global variables are a terribly bad idea.
Reason: You run the danger of your code being overwritten by any other JavaScript added to the
page after yours.
Workaround: use closures and the module pattern

Problem: all variables are global and can be accessed; access is not contained, anything in the page
can overwrite what you do.
var current = null;
var labels = {
'home': 'home',
'articles': 'articles',
'contact': 'contact'
};
function init() { };
function show() { current = 1; };
function hide() { show(); };

Module Pattern: You need to specify what is global and what isnt - switching syntax in between.
Keep consistent syntax and mix and match what to make global.

module = function () {
var current = null;
var labels = {
'home': 'home',
'articles': 'articles',
'contact': 'contact'
};
var init = function () { };
var show = function () { current = 1; };
var hide = function () { show(); }
return { init: init, show: show, current: current }
}();
module.init();

17) Avoid using with()


Using with() inserts a variable at the global scope. Thus, if another variable has the same name it
could cause confusion and overwrite the value.

18) Stick to a Strict Coding Style


Browsers are very forgiving JavaScript parsers. However, lax coding style will hurt you when you shift
to another environment or hand over to another developer. Valid code is secure code.
Validate your code: http://eslint.org/demo/

19) Comment as Much as Needed but Not More

[Note]: Use JSDoc Standard to document your code. Helps Code Editor Intelligence and Auto
documentation with tools like: DocumentationJS

Good code explains itself is an arrogant myth. Comment what you consider needed - but dont tell
others your life story. Avoid using the line comment //. /* */ is much safer to use because it doesnt
cause errors when the line break is removed.
If you debug using comments, there is a nice little trick:
module = function () {
var current = null;
/*
var init = function(){
};
var show = function(){
current = 1;
};
var hide = function(){
show();
}
// */
return { init: init, show: show, current: current }
}();
Comments should never go out to the end user in plain HTML or JavaScript.

20) Avoid Mixing with Other Technologies


JavaScript is good for calculation, conversion, access to outside sources (Ajax) and to define the
behavior of an interface (event handling). Anything else should be kept to the technology we have to
do that job.
FOR EXAMPLE:
Put a red border around all fields with a class of mandatory when they are empty.
var f = document.getElementById('mainform');
var inputs = f.getElementsByTagName('input');
for (var i = 0, j = inputs.length; i < j; i++) {
if (inputs[i].className === 'mandatory' && inputs.value === '') {
inputs[i].style.borderColor = '#f00';
inputs[i].style.borderStyle = 'solid';
inputs[i].style.borderWidth = '1px';
}
}

...Two months down the line: All styles must comply with the new company style guide; no borders
are allowed and errors should be shown by an alert icon next to the element.

People shouldnt have to change your JavaScript code to change the look and feel.
var f = document.getElementById('mainform');
var inputs = f.getElementsByTagName('input');
for (var i = 0, j = inputs.length; i < j; i++) {
if (inputs[i].className === 'mandatory' && inputs.value === '') {
inputs[i].className += ' error';
}
}
Using CSS inheritance, you can avoid having to loop over a lot of elements.
21) Use Shortcut Notations
Shortcut notations keep your code snappy and easier to read once you get used to it.

This code
var lunch = new Array();
lunch[0]='ADT';
lunch[1]='CNN';
lunch[2]='C01';
lunch[3]='Travelers';

Is the same as...


var lunch = ['ADT', 'CNN', 'C01', 'Travelers'];

This code
if(v){var x = v;} else {var x =10;}
Is the same as...
var x = v || 10;

This code
var direction;

if(x > 100){


direction = 1;
} else {
direction = -1;
}

Is the same as...


var direction = (x > 100) ? 1 : -1;

22) Modularize

[NOTE]: Use ES6 modules (import/export)

Keep your code modularized and specialized.


It is tempting and easy to write one function that does everything. However, as you extend the
functionality you will find that you do the same things in several functions.
To prevent that, make sure to write smaller, generic helper functions that fulfill one specific task
rather than catch-all methods.
At a later stage, you can also expose these functions when using the revealing module pattern to
create an API to extend the main functionality. Good code should be easy to build upon without
rewriting the core.

The Module Pattern uses functions and closures to provide encapsulation in functional languages like
JavaScript.

The Module pattern is the right solution if you want to:

encapsulate a chunk of code and a "class" doesn't make sense


provide public/private support (which "classes" don't support).

The Module Pattern tends to work well for a chunk of code that could be described as a Singleton
class.

23) Namespace your JavaScript if you need to refer to it elsewhere.

Your JavaScript shouldn't be floating off in the global namespace, where it collides with other stuff
you've included.

Although JavaScript doesn't have built-in notions of namespaces, its object model is flexible enough
that you can emulate them. Here's an example:

var MyNamespace = MyNamespace || {};

MyNamespace.MyModule = function()
{
// Your module is now in a namespace!
}

24) Anonymously scope JavaScript if youre never going to call it elsewhere.

[NOTE]: Use Arrow functions for IIFE (doesnt create a new scope)

(() => {
var x = 123;
console.log(x);
})();
Use IIFE to create UMD modules

(function (root, factory) {


if (typeof define === 'function' && define.amd) {
// AMD
define(['myAwesomePlugin'], factory);
} else if (typeof exports === 'object') {
// Node, CommonJS-like
module.exports = factory(require('myAwesomePlugin'));
} else {
// Browser globals (root is window)
root.myAwesomePlugin = factory(root.myAwesomePlugin);
}
}(this, function (myAwesomePlugin) {
// exposed public method
return {
id: 'this is my awesome plugin',
};
}));

If you're writing a one-off chunk of JavaScript that never needs to be referred to by other code, it's
wise to anonymously scope it so it won't get accidentally referenced elsewhere.

To do this, just wrap your code in an anonymous function closure:

// An anonymous function that can never be referenced by name...


(function(){
var x = 123;
console.log(x);
})(); // Call the anonymous function once, then throw it away!

console.log(x);

In the code above, the first console.log() will succeed, but the second will fail. You won't be able to
reference x outside of the anonymous function.

25) Allow for Configuration and Translation

Everything that is likely to change in your code should not be scattered throughout your code.
This includes labels, CSS classes, IDs and presets. By putting these into a configuration object and
making this one public we make maintenance easy and allow for customization.

26) Avoid heavy nesting


Nesting code explains its logic and makes it much easier to read, however nesting it too far can also
make it hard to follow what you are trying to do. Readers of your code shouldnt have to scroll
horizontally, or suffer confusion when their code editors wrap long lines (this makes your
indentation efforts moot anyway).
The other problem of nesting is variable names and loops. As you normally start your first loop
with i as the iterator variable, youll go on with j,k,l and so on. This can become messy quite quickly:
function renderProfiles(o){
var out = document.getElementById(profiles);
for(var i=0;i<o.members.length;i++){
var ul = document.createElement(ul);
var li = document.createElement(li);
li.appendChild(document.createTextNode(o.members[i].name));
var nestedul = document.createElement(ul);
for(var j=0;j<o.members[i].data.length;j++){
var datali = document.createElement(li);
datali.appendChild(
document.createTextNode(
o.members[i].data[j].label + +
o.members[i].data[j].value
)
);
nestedul.appendChild(datali);
}
li.appendChild(nestedul);
}
out.appendChild(ul);
}

Above using the generic really throw-away variable names ul and li here, We
need nestedul and datali for the nested list items. If the list nesting were to go even deeper I would
need more variable names, and so on and so on. It makes more sense to put the task of creating
nested lists for each member in its own function and call this with the right data. This also prevents
us from having a loop inside a loop.

The addMemberData() function is pretty generic and is very likely to come in handy at another time.
Taking these thoughts on board, we can rewrite the code as follows:

function renderProfiles(o){
var out = document.getElementById(profiles);
for(var i=0;i<o.members.length;i++){
var ul = document.createElement(ul);
var li = document.createElement(li);
li.appendChild(document.createTextNode(data.members[i].name));
li.appendChild(addMemberData(o.members[i]));
}
out.appendChild(ul);
}
function addMemberData(member){
var ul = document.createElement(ul);
for(var i=0;i<member.data.length;i++){
var li = document.createElement(li);
li.appendChild(
document.createTextNode(
member.data[i].label + +
member.data[i].value
)
);
}
ul.appendChild(li);
return ul;
}

Your functions should do one thing only on one level of abstraction. Functions should either do
something (modify) or answer something (query), but not both.

// DON'T
function getUserRouteHandler (req, res) {
const { userId } = req.params
// inline SQL query
knex('user')
.where({ id: userId })
.first()
.then((user) => res.json(user))
}

// DO
// User model (eg. models/user.js)
const tableName = 'user'
const User = {
getOne (userId) {
return knex(tableName)
.where({ id: userId })
.first()
}
}

// route handler (eg. server/routes/user/get.js)


function getUserRouteHandler (req, res) {
const { userId } = req.params
User.getOne(userId)
.then((user) => res.json(user))
}

27) Optimize loops

[NOTE]: Use Array methods for loop instead because they have their own scope, examples:
forEach, reduce, map, every

Loops can become very slow if you dont do them right. One of the most common mistake is to read
the length attribute of an array at every iteration:

var names = ['George','Ringo','Paul','John'];


for(var i=0;i<names.length;i++){
doSomeThingWith(names[i]);
}

This means that every time the loop runs, JavaScript needs to read the length of the array. You can
avoid that by storing the length value in a different variable:

var names = ['George','Ringo','Paul','John'];


var all = names.length;
for(var i=0;i<all;i++){
doSomeThingWith(names[i]);
}

An even shorter way of achieving this is to create a second variable in the pre-loop statement:

var names = ['George','Ringo','Paul','John'];


for(var i=0,j=names.length;i<j;i++){
doSomeThingWith(names[i]);
}

Another thing to ensure is that you keep computation-heavy code outside loops. This includes
regular expressions and more importantly DOM manipulation. You can create the DOM nodes
in the loop but avoid inserting them into the document.

28) Avoid long argument list


Use a single object parameter and destructuring assignment instead. It also makes handling optional
parameters much easier.
// DON'T
function getRegisteredUsers (fields, include, fromDate, toDate) { /* imple
mentation */ }
getRegisteredUsers(['firstName', 'lastName', 'email'], ['invitedUsers'], '
2016-09-26', '2016-12-13')

// DO
function getRegisteredUsers ({ fields, include, fromDate, toDate }) { /* i
mplementation */ }
getRegisteredUsers({
fields: ['firstName', 'lastName', 'email'],
include: ['invitedUsers'],
fromDate: '2016-09-26',
toDate: '2016-12-13'
})

29) Dont trust any data


One of the main points to bear in mind when talking about code and data security is not to trust any
data. This is not only about evil people wanting to hack your systems; it starts with plain usability.
Users will enter incorrect data, all the time. Not because they are stupid, but because they are busy,
distracted or the wording on your instructions is confusing them. For example, I just booked a hotel
room for a month rather than six days as I entered a wrong number I consider myself fairly smart.

In short, make sure that all the data that goes into your systems is clean and exactly what you need.
This is most important on the back end when writing out parameters retrieved from the URL. In
JavaScript, it is very important to test the type of parameters sent to your functions (using
the typeof keyword). The following would be an error if members is not an Array (for example for a
string itll create a list item for each character of the string):

function buildMemberList(members){
var all = members.length;
var ul = document.createElement('ul');
for(var i=0;i<all;i++){
var li = document.createElement('li');
li.appendChild(document.createTextNode(members[i].name));
ul.appendChild(li);
}
return ul;
}

In order to make this work, you need to check the type of members and make sure it is an array:

function buildMemberList(members){
if(typeof members === 'object' &&
typeof members.slice === 'function'){
var all = members.length;
var ul = document.createElement('ul');
for(var i=0;i<all;i++){
var li = document.createElement('li');
li.appendChild(document.createTextNode(members[i].name));
ul.appendChild(li);
}
return ul;
}
}

Arrays are tricky as they tell you they are objects. To ensure that they are arrays, check one of the
methods only arrays have. Another very insecure practice is to read information from the DOM and
use it without comparison. For example, I once had to debug some code that caused the JavaScript
functionality to break. The code that caused it was for some reason beyond me reading a user
name out of the innerHTML from a page element and calling a function with the data as a
parameter. As the user name could be any UTF-8 character this included quotation marks and single
quotes. These would end any string and the remaining part would be erroneous data. In addition,
any user changing the HTML using a tool like Firebug or Opera DragonFly could change the user
name to anything and inject this data into your functions.

The same applies to forms that validate only on the client side. I once signed up for an unavailable
email address by rewriting a select to provide another option. As the form wasnt checked on the
back end the process went through without a hitch.

For DOM access, check that the element you try to reach and alter is available and what you expect it
to be otherwise your code may fail or cause strange rendering bugs.

30) Keep DOM access to a minimum


Accessing the DOM in browsers is an expensive thing to do. The DOM is a very complex API and
rendering in browsers can take up a lot of time. You can see this when running complex web
applications when your computer is already maxed out with other work changes take longer or
get shown half way through and so on.

To make sure that your code is fast and doesnt slow down the browser to a halt try to keep DOM
access to a bare minimum. Instead of constantly creating and applying elements, have a tool
function that turns a string into DOM elements and call this function at the end of your generation
process to disturb the browser rendering once rather than continually.

31) Remove "Language"


Years ago, it wasn't uncommon to find the "language" attribute within script tags.
<script type="text/javascript" language="javascript">
...
</script>

However, this attribute has long since been deprecated; so leave it out.

32) Don't Use HTML Comments In Script Blocks


In the ancient days of javascript (1995), some browsers like Netscape 1.0 didn't have any support or
knowledge of the script tag. So when javascript was first released, a technique was needed to hide
the code from older browsers so they wouldn't show it as text in the page. The 'hack' was to use
HTML comments within the script block to hide the code.
Bad Practice
<script language="javascript">
<!--
// code here
//-->
</script>

33) Place Scripts at the Bottom of Your Page


the primary goal is to make the page load as quickly as possible for the user. When loading a script,
the browser can't continue until the entire file has been loaded. Thus, the user will have to wait
longer before noticing any progress.

If you have JS files whose only purpose is to add functionality -- for example, after a button is clicked
-- go ahead and place those files at the bottom, just before the closing body tag. This is absolutely a
best practice.

<p>And now you know my favorite kinds of corn. </p>


<script type="text/javascript" src="path/to/file.js"></script>
<script type="text/javascript" src="path/to/anotherFile.js"></script>
</body>
</html>

S-ar putea să vă placă și