Write clean JavaScript code ES6

Good code is not only the code that can run, but also the code that can be easily read, reused and reconstructed by others, because in addition to realizing functions, the code needs to be maintained by you or other team members most of the time.

Although this article mainly focuses on writing clean and tidy JavaScript ES6 Code and not related to any framework, but most of the examples mentioned below are also applicable to other languages. In addition, the following examples are mainly from Robert C. Martin's book Clean Code The recommendations adopted in the report do not mean strict compliance.

variable

Use meaningful names

The naming of variables should be descriptive and meaningful. The rule of thumb is that most JavaScript variables should use camel case.

// Error ❌
const foo = "JDoe@example.com";
const bar = "John";
const age = 23;
const qux = true;

// correct ✅
const email = "John@example.com";
const firstName = "John";
const age = 23;
const isActive = true

Note that boolean variable names usually seem to answer questions, for example:

isActive
didSubscribe
hasLinkedAccount

Avoid adding unnecessary context

There is no need to add redundant context in a specific object or class

// Error ❌
const user = {
  userId: "296e2589-7b33-400a-b762-007b730c8e6d",
  userEmail: "JDoe@example.com",
  userFirstName: "John",
  userLastName: "Doe",
  userAge: 23,
};

user.userId;

// correct ✅
const user = {
  id: "296e2589-7b33-400a-b762-007b730c8e6d",
  email: "JDoe@example.com",
  firstName: "John",
  lastName: "Doe",
  age: 23,
};

user.id;

Avoid hard coding

Make sure to declare meaningful and searchable constants instead of directly using a constant value. It is recommended to use snake naming_snake_case for global variables

// Error ❌
setTimeout(clearSessionData, 900000);

// correct ✅
const SESSION_DURATION_MS = 15 * 60 * 1000;

setTimeout(clearSessionData, SESSION_DURATION_MS);

function

Use descriptive naming

The function name can be long enough to describe its function. Generally, functions contain verbs to describe what it does, but the function that returns Boolean value is an exception. Generally, it is a question form that answers "yes" or "no". In addition, the function name should also be hump naming method.

// Error ❌
function toggle() {
  // ...
}

function agreed(user) {
  // ...
}

// correct ✅
function toggleThemeSwitcher() {
  // ...
}

function didAgreeToAllTerms(user) {
  // ...
}

Use default parameters

Using default values directly is more concise than short-circuit syntax or adding judgment statements to functions. It is worth noting that short-circuit syntax applies to all values considered false, such as false, null, undefined, '', '', '', 0 and NaN, while default parameters only replace undefined.

// Error ❌
function printAllFilesInDirectory(dir) {
  const directory = dir || "./";
  //   ...
}

// correct ✅
function printAllFilesInDirectory(dir = "./") {
  // ...
}

Limit the number of parameters

This article is controversial. There should be no more than 2 parameters of the function, which means that there are 0, 1 or 2 parameters of the function. If the third parameter is required, please explain:

  • Function needs to be split
  • Related parameters can be aggregated into objects and passed
// Error ❌
function sendPushNotification(title, message, image, isSilent, delayMs) {
  // ...
}

sendPushNotification("New Message", "...", "http://...", false, 1000);

// correct ✅
function sendPushNotification({ title, message, image, isSilent, delayMs }) {
  // ...
}

const notificationConfig = {
  title: "New Message",
  message: "...",
  image: "http://...",
  isSilent: false,
  delayMs: 1000,
};

sendPushNotification(notificationConfig);

Don't do too much in a function

In principle, a function only does one thing. This principle can help us reduce the volume and complexity of the function, and can also better test, debug and reconstruct. The number of lines of code of a function is an indicator to judge whether the function does too many things. Generally, it is recommended that the length of a function is less than 20 ~ 30 lines.

// Error ❌
function pingUsers(users) {
  users.forEach((user) => {
    const userRecord = database.lookup(user);
    if (!userRecord.isActive()) {
      ping(user);
    }
  });
}

// correct ✅
function pingInactiveUsers(users) {
  users.filter(!isUserActive).forEach(ping);
}

function isUserActive(user) {
  const userRecord = database.lookup(user);
  return userRecord.isActive();
}

Avoid using flag variables

The flag variable means that the function can be further simplified

// Error ❌
function createFile(name, isPublic) {
  if (isPublic) {
    fs.create(`./public/${name}`);
  } else {
    fs.create(name);
  }
}

// correct ✅
function createFile(name) {
  fs.create(name);
}

function createPublicFile(name) {
  createFile(`./public/${name}`);
}

Don't repeat yourself (DRY)

Repeated code is not a good signal. You copy and paste it N times. The next time you modify this part of the code, you will modify N places at the same time.

// Error ❌
function renderCarsList(cars) {
  cars.forEach((car) => {
    const price = car.getPrice();
    const make = car.getMake();
    const brand = car.getBrand();
    const nbOfDoors = car.getNbOfDoors();

    render({ price, make, brand, nbOfDoors });
  });
}

function renderMotorcyclesList(motorcycles) {
  motorcycles.forEach((motorcycle) => {
    const price = motorcycle.getPrice();
    const make = motorcycle.getMake();
    const brand = motorcycle.getBrand();
    const seatHeight = motorcycle.getSeatHeight();

    render({ price, make, brand, seatHeight });
  });
}

// correct ✅
function renderVehiclesList(vehicles) {
  vehicles.forEach((vehicle) => {
    const price = vehicle.getPrice();
    const make = vehicle.getMake();
    const brand = vehicle.getBrand();

    const data = { price, make, brand };

    switch (vehicle.type) {
      case "car":
        data.nbOfDoors = vehicle.getNbOfDoors();
        break;
      case "motorcycle":
        data.seatHeight = vehicle.getSeatHeight();
        break;
    }

    render(data);
  });
}

Avoid side effects

In JavaScript, the preferred mode should be functional rather than imperative. In other words, to ensure the purity of the function, the side effects can modify the shared state and resources, which will lead to code instability and difficult to test. Troubleshooting problems will also be particularly difficult. All side effects should be managed centrally. If you need to modify the global state, you can define a unified service to modify it.

// Error ❌
let date = "21-8-2021";

function splitIntoDayMonthYear() {
  date = date.split("-");
}

splitIntoDayMonthYear();

// Another function could be expecting date as a string
console.log(date); // ['21', '8', '2021'];

// correct ✅
function splitIntoDayMonthYear(date) {
  return date.split("-");
}

const date = "21-8-2021";
const newDate = splitIntoDayMonthYear(date);

// Original vlaue is intact
console.log(date); // '21-8-2021';
console.log(newDate); // ['21', '8', '2021'];

In addition, if a variable object is passed in as a function parameter, the parameter should be returned as a clone of the parameter rather than directly modified.

// Error ❌
function enrollStudentInCourse(course, student) {
  course.push({ student, enrollmentDate: Date.now() });
}

// correct ✅
function enrollStudentInCourse(course, student) {
  return [...course, { student, enrollmentDate: Date.now() }];
}

Concurrent

Avoid using callbacks

Callback function It's too messy, so ES6 provides us with Promise Allows us to use chained callbacks, of course Async/Await It provides a more concise scheme that allows us to write more linear code

// Error ❌
getUser(function (err, user) {
  getProfile(user, function (err, profile) {
    getAccount(profile, function (err, account) {
      getReports(account, function (err, reports) {
        sendStatistics(reports, function (err) {
          console.error(err);
        });
      });
    });
  });
});

// correct ✅
getUser()
  .then(getProfile)
  .then(getAccount)
  .then(getReports)
  .then(sendStatistics)
  .catch((err) => console.error(err));

// correct ✅✅
async function sendUserStatistics() {
  try {
    const user = await getUser();
    const profile = await getProfile(user);
    const account = await getAccount(profile);
    const reports = await getReports(account);
    return sendStatistics(reports);
  } catch (e) {
    console.error(err);
  }
}

error handling

Handle thrown exceptions and rejected Promise

Correctly handling exceptions can make our code easier to install and more convenient to troubleshoot problems.

// Error ❌
try {
  // Possible error codes
} catch (e) {
  console.log(e);
}

// correct ✅
try {
  // Possible error codes
} catch (e) {
  // Better than console.log
  console.error(e);

  // Notify users
  alertUserOfError(e);

  // Notification server
  reportErrorToServer(e);

  // Use custom exception handling
  throw new CustomError(e);
}

notes

Annotate only complex logic

Don't add too many comments, just add for complex logic.

// Error ❌
function generateHash(str) {
  // Hash variable
  let hash = 0;

  // Gets the length of the string
  let length = str.length;

  // Returns if the length is empty
  if (!length) {
    return hash;
  }

  // Traversal character
  for (let i = 0; i < length; i++) {
    // Get character code
    const char = str.charCodeAt(i);

    // Assign value to hash
    hash = (hash << 5) - hash + char;

    // Convert to 32-bit integer
    hash &= hash;
  }
}

// correct ✅
function generateHash(str) {
  let hash = 0;
  let length = str.length;
  if (!length) {
    return hash;
  }

  for (let i = 0; i < length; i++) {
    const char = str.charCodeAt(i);
    hash = (hash << 5) - hash + char;
    hash = hash & hash; // Convert to 32-bit integer
  }
  return hash;
}

version control

There's no need to write code modification history at all. Version management (such as git) has helped us do these things.

// Error ❌
/**
 * 2021-7-21: Fixed corner case
 * 2021-7-15: Improved performance
 * 2021-7-10: Handled mutliple user types
 */
function generateCanonicalLink(user) {
  // const session = getUserSession(user)
  const session = user.getSession();
  // ...
}

// correct ✅
function generateCanonicalLink(user) {
  const session = user.getSession();
  // ...
}

This article briefly discusses some improvements that can be made ES6 The principles and methods of code readability. Most of the principles can be applied to other programming languages. Using these principles may take more time, but in the long run, it can ensure the readability and scalability of your code.

Tags: Javascript

Posted on Sun, 05 Dec 2021 01:46:36 -0500 by tibiz