Coding Convention


Coding convention is a collection of rules set in place to ensure readability and manageability of codes. Especially because JavaScript is more flexible in terms of syntax (dynamic type, binding this, native object manipulation etc.), without a mutually agreed upon rules among developers, it will be near impossible to understand or to debug other's codes. Abiding by the coding convention increases readability and decreases chances of errors or bugs that might affect the performance of the program. For bigger projects, it is much more cost-efficient and reasonable to follow the coding conventions.

This document prioritizes readability and manageability given that it does not damage the performance of the overall program, and will strive to illuminate the ambiguity undetectable by ESLint (assuming linters like ESLint are used).

Table of Contents

Note This document deals with ES5 and higher, and is written based on ES6. Conventions needed for ES5 are marked with (ES5) tags.

Indentation

Do not mix spaces and tabs.

Depending on the development environment, indentation by tabs and spaces could look different, so if not unified, it makes the code harder to read. Therefore, before starting a project, the developer must choose between spaces and tabs. This can differ from project to project depending on individual preferences, and when using spaces, use either two or four spaces. When the indentation is all agreed upon, integrate it onto the editor for future use.

Note FE Development Lab uses two spaces. ESLint – Indent

End of Line

Maximum of one statement is permitted for each line, and a semicolon must be used at the end of each statement.

Although JavaScript does not strictly enforce this rule, it does frequently cause unintended errors and makes debugging more difficult.

Note ESLint – Indent

Naming Convention

Use camelCase.

Deciding the names of variables and functions is part of the coding convention. Most notable conventions are Camel Case, Pascal Case, Hungarian Notation, and Snake Case. Each has its pros and cons, and different languages ask for different conventions. In FE Development Lab, Camel Case is used.

Note ESLint – Camel Case

Do not use reserved words as variable names.
// Bad
let class;
let enum;
let extends;
let super;
let const;
let export;
let import;
For constants, use English capital letters (Snake Case).
SYMBOLIC_CONSTANTS;
For constructors, use capital Camel Case.
class ConstructorName {
  ...
};
Use Camel Case for variables and functions.
// Number, string, and a Boolean
let dog;
let variableName;

// Array - For arrays, use plural nouns
const dogs = [];

// Regular Expression - All regular expressions start with a 'r'
const rDesc = /.*/;

// Function
function getPropertyName() {
  ...
}

// Event Handler - All event handlers start with 'on'
const onClick = () => {};
const onKeyDown = () => {};

// Returning Boolean - All functions that return a Boolean start with 'is'
let isAvailable = false;
Local (or private) variables start with '_'.
let _privateVariableName;
let _privateFunctionName;

// for Objects
const customObjectName = {};
customObjectName.propertyName;
customObjectName._privatePropertyName;
_privateCustomObjectName;
_privateCustomObjectName._privatePropertyName;
let _privateVariableName;
let _privateFunctionName;

// If it is an object
const customObjectName = {};
customObjectName.propertyName;
customObjectName._privatePropertyName;
_privateCustomObjectName;
_privateCustomObjectName._privatePropertyName;
Commonly capitalized abbreviations like URL and HTML are used as are.
parseHTML
parseXML

Global Variables

Do not use global variables.

JavaScript is built based on global variables. Therefore, every compile takes place in a single shared global (window) object. Granted, global variable appears to be convenient because it can be accessed anywhere in the program, but it also means that global variables can be changed anywhere in the program. This can cause critical errors in running the program.

// Bad
myglobal = "hello";
Do not use implicit global variables.
// Bad
function sum(x, y) {
  result = x + y;
  return result;
}

// Bad
function foo() {
  let a = b = 0; // This is same as let a = (b = 0), where 'b' becomes implicitly global.
}
// Good
function sum(x, y) {
  let result = x + y;
  return result;
}

// Good
function foo() {
  let a, b;
  a = b = 0;
}

Declaration and Assignment

Variables

Use let for values that change, and const for values that do not. Never use var.

When a const variable is declared, it tells the program that “this variable will never take a new value,” and makes the code easier to read and to maintain. let keyword assigns variables within the block and is assigned in block scopes like in many other programming languages, and can help prevent errors.

Note ESLint – no-var

Declare const before let.

Declaring const before let makes codes to be more organized and easy to read.

Note ESLint – Prefer-const

// Bad - No Grouping
let foo;
let i = 0;
const len = this._array.length;
let bar;

// Good
const len = this._array.length;
const len2 = this._array2.length;
let i = 0;
let j = 0;
let foo, bar;
Declare and assign const and let at the point of first usage.

Variables declared using const and let take the block scope, so are not subjected to hoisting.

// Bad - Declared variables outside of the block scope
function foo() {
  const len = this._array.length;
  let i = 0;
  let j = 0;
  let len2, item;

  for (; i < len; i += 1) {
      ...
  }
  
  len2 = this._array2.length;
  for (j = 0, len2 = this._array2.length; j < len2; j += 1) {
      item = this._array2[j];
      ...
  }
}

// Good 
function foo() {
  const len = this._array.length;
  for (let i = 0; i < len; i += 1) {
      ...
  }

  // Declared at point of use
  const len2 = this._array2.length;
  for (let j = 0; j < len2; j += 1) {
      const item = this._array2[j];
      ...
  }
}
Differentiate outside modules from inside modules.

When referencing both outside modules and inside modules, it increases readability if an empty line is placed between declarations.

const lodash = require('lodash');
const $ = require(jquery);
const handlebars = require('handlebars');
const d3 = require('d3');

const pluginFactory from '../../factories/pluginFactory';
const predicate from '../../helpers/predicate';
const raphaelRenderUtil from '../../plugins/raphaelRenderUtil';
Do not self-assign.

Self-assigning does nothing, and could be signs of an error caused by incomplete refactoring.

Note ESLint – no-self-assign

// Bad
foo = foo;

[a, b] = [a, b];

[a, ...b] = [x, ...b];

({a, b} = {a, x});

// Good
foo = bar;

let foo = foo;

[foo = 1] = [foo];
When using var, always declare at beginning of the function scope. (ES5)

Although JavaScript utilizes code blocks, it does not provide an actual namespace for blocks. Therefore, as long as a variable is declared within a block, it can be called from anywhere in the block. This is because hoisting happens internally when JavaScript compiles, and it damages readability and complicates debugging.

Note ESLint – vars-on-top ESLint - no-inner-declarations

// Bad - Variable is declared in the middle of the scope
function foo() {
  ...
  var bar = '';
  var quux = '';
}

// Good
function foo() {
  var bar = '';
  var quux = '';
  ...
}
In ES5 Environment, variables must be declared with var keyword and should be assigned as they are declared. (ES5)

If a single var is used to declare multiple variables, the code can easily become unorganized, so FE Development Lab always uses one var per variable.

// Bad - Single var is used to declare multiple variables
var foo = '',
  bar = '',
  quux = '';

// Good - One var is used for each declaration
var foo = '';
var bar = '';
var quux = '';
When using var, variables should not be declared in block scopes. (ES5)

Variables declared with var live in function scopes. If a variable is declared inside for statements and if statements, for example, it is possible to mistake the scope as a block, causing unintended errors.

// Bad
var length = 100;
for (var i = 0; i < length; i += 1) {
  ...
}

// Good
var length = 100;
var i;
for (i = 0; i < length; i += 1) {
  ...
}

// Good
var i = 0;
var length = 100;
for (; i < length; i += 1) {
  ...
}
It is permissible to declare and assign the var variable separately if the gap between the declaration and the point of use compromises readability. (ES5)

If the gap between the declaration and the actual use is too large, thereby causing readability issues, it is allowed to declare and assign separately. In this case, too, the variable must be declared at the beginning of the scope.

// Bad
function foo() {
  var i = 0;
  var len = this._array.length;

  for (; i < len; i += 1) {
    ...
  }

  // Use of var limited within the statement
  for (var j = 0, len2 = this._array2.length; j < len2; j += 1) {
    // Use of var limited within the statement
    var item = this._array2[j];
    ...
  }
}

// Good - Declared and assigned separately
function foo() {
  var i;
  var j;
  var len;
  var len2;
  var item;

  i = 0;
  len = this._array.length;
  for (; i < len; i += 1) {
    item = this._array[i];
    ...
  }

  // Declare at the beginning, and assign at the point of use
  j = 0;
  len2 = this._array2.length;
  for (; j < len2; j += 1) {
    item = this._array2[j];
    ...
  }
}
If there is a conditional operation in the middle of a function, declare the var variables that are used after the conditional in the beginning, and assign at the point of use. (ES5)

Such is also a case where the large gap between declaration and the point of use may compromise the readability; it is permissible to assign at the point of use.

// Bad
function foo(isValid) {
  var i = 0;
  var len = this._array.length;

  if (!isValid) {
    return false;
  }

  for (; i < len; i += 1) {
    ...
  }
}

// Good
function foo(isValid) {
  var i, len;

  if (!isValid) {
    return false;
  }

  // Declare at the beginning of the scope, and assign at the point of use.
  i = 0;
  len = this._array.length;
  for (; i < len; i += 1) {
    ...
  }
}
When declaring and assigning separately, it is permissible to declare multiple variables using a single var. (ES5)

Because using a single var to declare multiple variables over multiple lines may make the code to appear unorganized, declare everything in one line.

// Bad - Unnecessarily used multiple lines
var foo,
  bar,
  quux;

// Good - Declared in one line
var foo, bar, quux;

// Good
var foo;
var bar;
var quux;
Declare variables that are assigned, first. (ES5)

When some variables are assigned and some are only declared, it can be helpful to group them separately, and declare variables with assignments first.

// Bad
var foo;
var bar;
var qux;
var i = 0;
var j = 0;
var len = this._array.length;
var len2 = this._array2.length;
var item;

// Bad
var i = 0, length = ary.length, j, k;

// Good
var i = 0;
var j = 0;
var len = this._array.length;
var len2 = this._array2.length;
var foo, bar, quux, item;

Arrays and Objects

Arrays and objects must be declared by literals.

Literal notations are shorter than constructor functions and can decrease the possibility of errors.

Note ESLint – no-new-object

// Bad
const emptyArr = new Array();
const arr = new Array(1, 2, 3, 4, 5);

// Bad - Used object constructor
const emptyObj = new Object();
const obj = new Object();

// Good
const emptyArr = [];
const arr = [1, 2, 3, 4, 5];

// Good
const emptyObj = {};
const obj = {
  pro1: 'val1', 
  pro2: 'val2'
};
Do not use an iterator to clone an array.

When cloning a complex object, using a spread operator is more precise and more readable than using an iterator.

Note mdn – Spread Operator

const len = items.length;
let i;

// Bad
for (i = 0; i < len; i++) {
  itemsCopy[i] = items[i];
}

// Good
const itemsCopy = [...items];

For ES5 environments, use Array.prototype.slice. (ES5)

// Good
itemsCopy = items.slice();
If the beginning of an array was a new line, the end of the array must also be a new line.

Consistent lining convention increases code readability among cooperating developers.

Note ESLint – array-bracket-newline

// Bad
var a = [1
];

// Good
var c = [1];
var d = [
    1
];
If even one element of the array has a line change, every element in the array must be consistent.

Note ESLint – array-element-newline

// Bad
const d = [1,
  2, 3];
const e = [
  function foo() {
    dosomething();
  }, function bar() {
    dosomething();
  }
];

// Good
const a = [1, 2, 3];
const b = [
  1, 
  2, 
  3
];
Single line programming is only allowed for objects with one property, and for objects with two or more properties, line changes are mandatory.

Note ESLint – object-property-newline

// Bad - Needs line change
const obj = {foo: 'a', bar: 'b'}

// Good
const obj = {foo: 'a'};

// Good
const obj = {
  foo: 'a'
};
When using an object literal to define an object, there cannot be a white space in front of the colon, but a white space after the colon is mandatory.
// Bad
var obj = {
  foo : 'a'
}

// Good
var obj = {
  foo: 'a'
}
Use method shorthand to define a method within an object.

Shorthand can be used to define complex object literals in a concise manner.

Note ESLint - object-shorthanded

// Bad
const atom = {
  value: 1,

  addValue: function(value) {
    return atom.value + value;
  }
};

// Good
const atom = {
  value: 1,

  addValue(value) {
    return atom.value + value;
  }
};
When using the Method Syntax, separate each class member by empty lines.

Note ESLint – lines-between-class-members

// Bad
class MyClass {
  foo() {
    //...
  }
  bar() {
    //...
  }
}

// Good
class MyClass {
  foo() {
    //...
  }

  bar() {
    //...
  }
}

Function

Do not use function constructors.

When a string is passed to the function constructor, the engine parses it using the eval function and compromises compilation speed.

Note ESLint - no-new-func

// Bad - Used function constructor
const doSomething = new Function('param1', 'param2', 'return param1 + param2;');

// Good - Declared a function
function doSomething(param1, param2) {
  return param1 + param2;
}

// Good - Used method syntax
const doSomething = function(param1, param2) {
  return param1 + param2;
};
Function must be declared before use, and function declaration should follow variable declaration.

Because when the function created by function expression is hoisted, the value is not assigned to the function, and an error will occur if a function is used before declaration.

// Bad - Used before declaration
const sumedValue = sum(1, 2);
const sum = function(param1, param2) {
  return param1 + param2;
};

// Bad - Used before declaration
const sumedValue = sum(1, 2);
function sum(param1, param2) {
  return param1 + param2;
};

// Good
const sum = function(param1, param2) {
  return param1 + param2;
};
const sumedValue = sum(1, 2);
Immediately Invoked Function Expression (IIFE) should only be used in accordance to the convention.

The grouping operator () used in IIFE, while useful, can cause readability issues, so it is recommended to consistently follow the style presented below.

// Bad
(function() {
  ...
})();

// Good
(function() {
  ...
}());
Function declarations may not be used in block scopes. (ES5)

JavaScript before ES6 operated under function scopes. The function declared with a declaration inside of the block scope can be mistaken for only being valid in the block even though it takes the function scope. On the other hand, for a function declared using the function expression inside a block scope, the declaration itself is hoisted to the top, but the assignment takes place within the block. Therefore, it does not leave any room for mistakes.

Note ESLint - no-inner-declarations

// Bad
if (condition) {
  function someFunction() {
  
  }
} else {
  function someFunction() {
  
  }
}

// Good
var someFunction;

if (condition) {
  someFunction = function() {
    ...
  }
} else {
  someFunction = function() {
    ...
  }
}

Arrow Function

Use arrow functions instead of function expressions.

Arrow functions are automatically bound to higher context without extra binding this, so arrow functions are less verbose and more readable.

Note ESLint – prefer-arrow-callback

// Bad
[1, 2, 3].map(function (x) {
  const y = x + 1;
  return x * y;
});

// Good
[1, 2, 3].map(x => {
  const y = x + 1;
  return x * y;
});
If the arrow function only takes one parameter, the parenthesis can be omitted.

By omitting the parenthesis when only one parameter is required, developers can better benefit from the functionalities of arrow functions.

Note ESLint – arrow-parens

// Bad
[1, 2, 3].map((x) => {
  const y = x + 1;
  return x * y;
});

// Good
[1, 2, 3].map(x => x * x);

// Good
[1, 2, 3].reduce((y, x) => x + y);
Take advantage of implicit returns.

If the body of a function consists of a single expression, the curly braces {…} may be omitted by using the implicit return. In other cases, return statement may not be omitted.

// Bad
[1, 2, 3].map(number => {
  const nextNumber = number + 1;
  `A string containing the ${nextNumber}.`;
});

// Good - Used implicit return
[1, 2, 3].map(number => `A string containing the ${number + 1}.`);
When using an implicit return, linebreak is not allowed before the function body.

By not using a linebreak before the function body, it makes it easier to differentiate between a mistakenly omitted return statement and an implicit return.

Note ESLint – implicit-arrow-linebreak

// Bad
(foo) =>
  bar;

(foo) =>
  (bar);

(foo) =>
  bar =>
    baz;

// Good
(foo) => bar;

(foo) => (bar);

(foo) => bar => baz;

(foo) => (
  bar()
);

Promise Executor

Promise executor may not include an async function.

async makes it so that the error ‘thrown’ by the asynchronous executor cannot be ‘caught,’ and it also complicates debugging because a promise is never rejected.

Note ESLint – no-asnyc-promise-executor

// Bad
const result = new Promise(async (resolve, reject) => {
  resolve(await foo);
});

// Good
const result = new Promise((resolve, reject) => {
  readFile('foo.txt', function(err, result) {
    if (err) {
      reject(err);
    } else {
      resolve(result);
    }
  });
});

Destructuring

When accessing object’s properties, use destructuring.

Destructuring provides an intuitive and logical way to extract the necessary values from an object, and increases the code readability.

Note ESLint – prefer-destructuring

// Bad
function getFullName(user) {
  const firstName = user.firstName;
  const lastName = user.lastName;

  return `${firstName} ${lastName}`;
}

// Bad
const first = arr[0];
const second = arr[1];

// Good
function getFullName(obj) {
  const {firstName, lastName} = obj;

  return `${firstName} ${lastName}`;
}

// Good
const [first, second] = arr;

// Good
function getFullName({firstName, lastName}) {
  return `${firstName} ${lastName}`;
}
When changing the name of a variable, destructuring is optional.
// Good
const changeFirstName = user.firstName;

// Good
const {firstName: changeFirstName} = user;

Template Literal

When combining variables to create a string, use template literal.

Template literal decreases the complexity of the code by providing an elegant way to handle string concatenation.

Note ESLint – prefer-template

// Bad
function sayHi(name) {
  return 'How are you, ' + name + '?';
}

// Bad
function sayHi(name) {
  return ['How are you, ', name, '?'].join();
}

// Bad - Use regular quotation marks when not using template literal
function sayHi(name) {
  return `How are you name?`;
}

// Good
function sayHi(name) {
  return `How are you, ${name}?`;
}

Classes and Constructors

Use extends with class to generate objects and inheritances.

The extends method provides much simpler syntax than creating inheritances based on prototypes.

// Bad
function Queue(contents = []) {
  this._queue = [...contents];
}
Queue.prototype.pop = function() {
  const value = this._queue[0];
  this._queue.splice(0, 1);
  return value;
};

// Good
class Queue {
  constructor(contents = []) {
    this._queue = [...contents];
  }
  pop() {
    const {value} = this._queue;
    this._queue.splice(0, 1);
    return value;
  }
}
Except when using mixin, do not explicitly call prototypes.

Follow the previously agreed upon rules to extend the object to write predictable code.

// Bad
const inherits = require('inherits');
function PeekableQueue(contents) {
  Queue.apply(this, contents);
}
inherits(PeekableQueue, Queue);
PeekableQueue.prototype.peek = function() {
  return this._queue[0];
};

// Good
class PeekableQueue extends Queue {
  peek() {
    return this._queue[0];
  }
}

Module

Always use import and export.

When other methods to integrate modules are used, the code consistency is compromised.

// Best
import {es6} from './AirbnbStyleGuide';
export default es6;

// Bad
const AirbnbStyleGuide = require('./AirbnbStyleGuide');
module.exports = AirbnbStyleGuide.es6;

// Good
import AirbnbStyleGuide from './AirbnbStyleGuide';
export default AirbnbStyleGuide.es6;
Do not use wildcard import *.

For reasons such as having to support with syntax, wildcard import can cause identifier corruption every time the module changes if the name is not set.

// Bad
import * from './AirbnbStyleGuide';

// Good
import * as AirbnbStyleGuide from './AirbnbStyleGuide';
Do not export directly from the import statement.

Although it may look concise, maintain the code consistency by distinctively separating imports and exports.

// Bad
export {es6 as default} from './airbnbStyleGuide';

Block Structures

Even when dealing with a single line block, do not omit the curly braces {…} and use clear line changes.

When the block only consists of one line, it is possible to skip the curly braces {...}, but it makes the code structure awkward. Although it is possible, it raises the possibility of future errors, and becomes a not-so-dormant threat to the code.

Note ESLint – brace-style ESLint – curly

// Bad
if(condition) doSomething();

// Bad
if (condition) doSomething();
else doAnything();

// Bad
for(let prop in object) someIterativeFn();

// Bad
while(condition) iterating += 1;

// Good
if (condition) {
  ...
}

// Good
if (condition) {
  ...
} else {
  ...
}
Leave empty spaces between a keyword and a conditional statement.

Without the empty spaces between keywords and conditional statements, the code becomes too compact and hard to read.

Note ESLint – keyword-spacing

// Bad
var i = 0;
for(;i<100;i+=1) {
  someIterativeFn();
} 

// Good
var i = 0;
for(; i < 100; i+=1) {
  someIterativeFn();
} 
When using a do-while statement, the semicolon ; follows the while statement.
// Bad
do statement while(condition)

// Good
do {
  ...
} while (condition);
When using a switch-case, except for the first case, use line changes before each case statement.
// Good
switch (value) {
  case 1:
    doSomething1();
    break;

  case 2:
    doSomething2();
    break;

  case 3:
    return true;

  default:
    throw new Error('This shouldn\'t happen.');
}
When using switch-case, each case must end with one of break, return, or throw, and if it does not have a default statement, // no default comment must be included.

If multiple cases serve one purpose, it is permissible to not include the break, but if the purpose varies even in the slightest, include the break and edit the code differently.

Note ESLint – no-fallthrough

// Bad - breaks are not included even though cases 1 and 2 serve different purposes
switch (value) {
  case 1:
    doSomething1();

  case 2:
    doSomething2();
    break;

  case 3:
    return true;

  // no default
}

// Bad - nothing hints at 'no default' even if it does not include a default
switch (value) {
  case 1:
    doSomething1();
    break;

  case 2:
    doSomething2();
    break;

  case 3:
    return true;
}

// Good - if multiple cases serve the same purpose, it is permissible to not include the break
switch (value) {
  case 1:
  case 2:
    doSomething();
    break;

  case 3:
    return true;

  // no default
}

Data Type Checks

Use only the previously agreed upon type checks.

Predetermined type checkers make codes more predictable. It is recommended to use FE Development Lab’s code utility, the TOAST UI CodeSnippet.

// String
typeof variable === 'string'
tui.util.isString(variable)


// Number
typeof variable === 'number'
tui.util.isNumber(variable)


// Boolean
typeof variable === 'boolean'
tui.util.isBoolean(variable)


// Object
typeof variable === 'object'
tui.util.isObject(variable)


// Array
Array.isArray(arrayObject)
tui.util.isArray(variable)


// Null
variable === null
tui.util.isNull(variable)


// Undefined
typeof variable === 'undefined'
variable === undefined
tui.util.isUndefined(variable)


// Element Node
element.nodeType === 1
tui.util.isHTMLNode(element)

Testing Conditions

Only use === and !== to test for strict equality.

Using == or != creates ambiguity within the conditional statement, because it disregards the types of data, and can lead to type coercion.

Note ESLint - eqeqeq

const numberB = 777;

// Bad
if (numberB == '777') {
  ...
}

// Good
if (numberB === 777) {
  ...
}
Use predetermined methods to test conditions.

By using predetermined methods to test conditions, the code becomes more predictable. It is recommended to use FE Development Lab’s code utility, TOAST UI CodeSnippet.

// String - isNotEmptyString?
if (string) ...
if (tui.util.isNotEmpty(string)) ...

// String - isEmptyString?
if (!string) ...
if (tui.util.isEmpty(string)) ...

// Array - isNotEmpty?
if (array.length) ...
if (tui.util.isNotEmpty(array)) ...

// Array - isEmpty?
if (!array.length) ...
if (tui.util.isEmpty(array)) ...

// Object - isNotEmpty?
if (tui.util.isNotEmpty(object)) ...

// Object - isEmpty?
if (tui.util.isEmpty(object)) ...

// is value assigned?
if (tui.util.isExisty(variable)) ...

// is variable true?
if (variable) ...

// is variable false?
if (!variable) ...

Return

Each function should return only once.

The return value should be returned only once at the end of the function. However, if the function has a conditional statement, this does not necessarily have to be true. By following such convention, functions become more predictable and concise.

// Bad
function getResult() {
  ...
  if (condition) {
    ...
    return someDataInTrue;
  }
  ...
  return someDataInFalse;
}

// Allow
function foo(isValid) {
  ...
  // conditional statement
  if (!isValid) {
    return;
  }
  ...
  
  return someDataInTrue;
}

// Good
function getResult() {
  let resultData;
  ...

  if (condition) {
    ...
    resultData = someDataInTrue;
  } else {
    ...
    resultData = someDataInFalse;
  }

  return resultData;
}
Always leave an empty line before the return statement.

When a return is written directly after other commands, it makes the code difficult to read. Therefore, always leave an empty line before the return statement.**

Note ESLint – padding-line-between-statement

// Bad
function getResult() {
  ...
  return someDataInFalse;
}

// Good
function getResult() {
  ...

  return someDataInFalse;
}

Iterate

Generalized iterator methods decrease the possibility of a mistake.

// Good
var i, len
for (i = 0, len = array.length; i < len; i += 1) ...

// Good
[1, 2, 3].forEach(array, function(value, index) {
  ...
});

If the environment does not support iterator methods, use iterator methods provided by code-snippet.js. (ES5)

// Good
tui.util.forEachArray(array, function(value, index) {
  ...
});
Always conduct the hasOwnProperty check within the for-in statement.

Unintentionally inherited properties could cause problems within the code.

// Good
for (const prop in object) {
  if (object.hasOwnProperty(prop)) {
    ...
  }
}
Declare the counter first. (ES5)

By declaring the counter first, it helps to prevent issues caused by the variable executing without having been reset.

// Bad
for (var i = 0; i < array.length; i += 1) ...

// Bad
for (var i in array) ...

// Good
var i, len
for (i = 0, len = array.length; i < len; i += 1) ...

// good
var key;
for (key in object) ...

Scope of Callback Function

When using anonymous functions as callback functions, try not to use closures, and declare variables appropriately within each scope.

When unnecessary closures are used, the increased number of scope chain references can compromise the performance and the readability.

// bad
let data1, data2, ...;

forEach(arr, function() {
  data1 = someFunction1();
  data2 = someFunction2();
  ...
});

// Allow
function foo() {
  const length = ary.length;
  let i = 0;
    ...

  forEach(ary, function(data1, data2) {
    ...

    // Variable declared out of scope out of necessiry; closure allowed.
    i += (length + 2);
    ...
  });
}

// Good
function foo() {
  ...

  // Variable declared within the anonymous function scope. 
  forEach(ary, function(data1, data2) {
    const data1 = someFunction1(data1);
    const data2 = someFunction2(data2);
  ...
  });
}

Comment

Comments should be indented according to the structure of the code that they are explaining.
// Bad
function someFunction() {
  ...

// Comments on statements
  statements
}

// Good
function someFunction() {
  ...

  // Comments on statements
  statements
}
When writing comments at the end of the statement, use empty spaces followed by a single line comment.
// Bad
var someValue = data1;//Needs empty space before and after the comment

// Bad
var someValue = data1; /* multiline comment */

// Good
var someValue = data1; // Appropriate empty spaces before and after comment
When using multiline comment, synchronize the * indentation. Leave the first and last line of the comment empty.
// Bad - '*' Inappropriate indentation
/*
* Comments
*/

// Bad - Disallow commenting on the first line
...
/* var foo = '';
 * var bar = '';
 * var quux;
 */

// Good - '*' Appropriate indentation
/*
 * Comments
 */
To comment out an entire code block, use single line comment structure.
// Bad - Used multline comments
...
/*
 * var foo = '';
 * var bar = '';
 * var quux;
 */

// Good - Used single line comments
...
// var foo = '';
// var bar = '';
// var quux;

White Space

Include white space in between keywords, operators, and other codes.

Codes compact with operators and keywords are difficult to read.

// Bad
var value;
if(typeof str==='string') {
  value=(a+b);
}

// Good
var value;
if (typeof str === 'string') {
  value = (a + b);
}
There cannot be white spaces immediately after the starting parenthesis and before the ending parenthesis.
// Bad - Inappropriate white spaces
if ( typeof str === 'string' )

// Bad - Inappropriate white spaces
var arr = [ 1, 2, 3, 4 ];

// Good
if (typeof str === 'string') {
  ...
}

// Good
var arr = [1, 2, 3, 4];
There should always be a white space after a comma if a value follows.

Separating values and commas with white spaces increases readability. At FE Development Lab a white space is always included after the comma.

// Bad - No white space
var arr = [1,2,3,4];

// Good
var arr = [1, 2, 3, 4];

Afterword

So far, this guide has discussed the coding convention. Coding convention is a must-have set of rules for JavaScript projects. This guide documents the basic JavaScript styles used at FE Development Lab. It is possible to integrate even more sophisticated coding convention if a project calls for it. It is the author’s hope that this guide helps developers write codes that are readable and manageable.


The employee trainings provided at FE Development Lab related to this document are as listed below. It is recommended to take these courses as well.

  • Basics of JavaScript
  • Object-oriented Programming with JavaScript
  • Real World JavaScript Development using Webpacks
  • Front-end Performance

This document is an official Web Front-End development guide written and maintained by NHN Cloud. FE Development Lab. Any errors, questions, and points of improvement pertaining to this document should be addressed to the official support channel (dl_javascript@nhn.com).


Last Modified
2019. 03. 29
FE Development LabBack to list