JavaScript best practices for writing clean code. Work with Object

alexandrshy

Alex Shulaev

Posted on June 19, 2020

JavaScript best practices for writing clean code. Work with Object

Accepting style guides or coding conventions helps make your code cleaner and more predictable.

The more detailed you describe the rules for your project, the easier it will be for another developer to write code according to your vision.

Now there is a large selection of ready-made style guides from various companies and developers posted in open source. In this article I want to give my advice on adding rules for working with Objects. You can consider this as recommendations, or you can configure ESLint to automatically check these rules.

Object creation

Use literal syntax to create objects. This is a shorter analog of Object constructor.

// bad
const product = new Object();

// good
const product = {};

ESLint rule: no-new-object

How to add to your config:

"rules": {
  "no-new-object": "error"
}

Object shorthand

Use shorthand for property value and methods. This recording option is intuitive and a shorter counterpart.

// bad
const product = {
  name: name,
  id: id,
  getPrice: function () {},
  generateSpecialOffer: function* () {},
};

// good
const product = {
  name,
  id,
  getPrice() {},
  *generateSpecialOffer() {},
};

ESLint rule: object-shorthand

How to add to your config:

"object-shorthand": [
  "error",
  "always",
  {
    "ignoreConstructors": false,
    "avoidQuotes": true
  }
]

Quotes

Property names of objects can be written with or without quotes, both of these options are valid. By analogy with the previous rules, we can decide not to use quotes as this is a shorter record, but in fact, it is not quite so, the fact is that we have cases when the presence of quotes is mandatory. For example, when a property name has space or dash. However, this is not so common, so using quotes for all properties will be irrational. The best option is to use quotes only when it is necessary.

// bad
const product = {
  "description": "",
  "short-description": "",
};

// good
const product = {
  description: "",
  "short-description": "",
};

ESLint rule: quote-props

How to add to your config:

"quote-props": ["error", "as-needed", { "unnecessary": true }]

Object.prototypes

The Object.create() method creates a new object, using an existing object as the prototype of the newly created object. This allows you to override methods (e.g. hasOwnProperty), which can break the logic of the application. To solve this problem, you need to always call methods from Object.prototype

// bad
const product = {
  name: "name-1",
  id: 1,
};

console.log(product.hasOwnProperty("name")); // true?

// good
const product = {
  name: "name-1",
  id: 1,
};

console.log(Object.prototype.hasOwnProperty.call(product, "name")); // true

ESLint rule: no-prototype-builtins

How to add to your config:

"no-prototype-builtins": "error"

The "extends": "eslint:recommended" property in a configuration file enables this rule.

Destructuring

Destructuring is now very popular and indeed it has proven itself in a much more convenient way than accessing a property through expression. The main advantage of destructuring is a shorter code entry. You no longer need to create additional variables to store properties

const product = {
  name: "name-1",
  id: 1,
  price: "100$",
};

// bad
const getPrice = (product) => {
  const price = product.price;
  return `Full price: ${price}`;
};

// good
const getPrice = ({ price }) => `Full price: ${price}`;

ESLint rule: prefer-destructuring

How to add to your config:

"prefer-destructuring": [
  "error",
  {
    "VariableDeclarator": {
      "array": false,
      "object": true
    },
    "AssignmentExpression": {
      "array": true,
      "object": false
    }
  },
  {
    "enforceForRenamedProperties": false
  }
]

Object spread

If you have a task to combine several objects or get a copy of objects, you need to use Object.assign with special care (or even better, use Spread syntax instead). Let's look at a simple example right away

// bad
const product = {
  name: "name-1",
  id: 1,
};
const copyProduct = Object.assign(product, { name: "name-2", id: 2 });

console.log("copyProduct", copyProduct); // {name: "name-2", id: 2}
console.log("product", product); // {name: "name-2", id: 2}

As a result, we got a mutation of the first object. This is not a bug, everything works as it should if you read about Object.assign(), but the problem is that it creates situations where the developer can forget about this behavior and get an unexpected result. To get the expected result without mutations you need to do

// still not very good
const product = {
  name: "name-1",
  id: 1,
};
const copyProduct = Object.assign({}, product, { name: "name-2", id: 2 });

console.log("copyProduct", copyProduct); // {name: "name-2", id: 2}
console.log("product", product); // {name: "name-1", id: 1}

Here we solved the problem with the mutation, but this structure is very wordy. We have a better option

// good
const product = {
  name: "name-1",
  id: 1,
};
const copyProduct = { ...product, name: "name-2", id: 2 };

console.log("copyProduct", copyProduct); // {name: "name-2", id: 2}
console.log("product", product); // {name: "name-1", id: 1}

With spread syntax, we were able to get rid of an unexpected mutation and the solution became much more concise.


Thank you for reading! This was a spontaneous article, now I'm writing a series of articles about the development of a side project, take a look if you are interested in it. See you soon 👋

💖 💪 🙅 🚩
alexandrshy
Alex Shulaev

Posted on June 19, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related