Ten JS tips you don't know

Posted by Vasudhevan on Sun, 02 Jan 2022 11:49:36 +0100

Summed up some commonly used JS tips to make your code more elegant!

1. Use const definition

Don't over declare variables in development, and try to use expressions and chain calls. Then, if you can use , const , don't use , let. After you write too much about this mode, you will find that you can hardly find several places to use "let" in the project:

// bad
let result = false;
if (userInfo.age > 30) {
  result = true;
}
// good
const result = userInfo.age > 30;
Copy code

Many colleagues write this in the project,

handleFormChange(e) {
  let isUpload;
  if (e.target.value === 'upload') {
    isUpload = true;
  } else {
    isUpload = false;
  }
}
Copy code

But in fact, the expressions of = = and = = = can directly assign values to variables:

handleFormChange(e) {
  const isUpload = (e.target.value === 'upload');
}
Copy code

If you want to reverse, it is also very simple:

handleFormChange(e) {
  const isManual = (e.target.value !== 'upload');
}
Copy code

2. Conditionally add attributes to objects

You can use the expand operator to conditionally add attributes to an object:

const condition = true;
const person = {
  id: 1,
  name: "dby",
  ...(condition && { age: 12 }),
};
Copy code

If {condition} is true, {age: 16} will be added to the object; If "condition" is "false", it is equivalent to expanding "false", which will not have any impact on the object

3. Conditionally add elements to the array

This is the source code of Webpack configuration in CRA:

module.exports = {
  plugins: [
    new HtmlWebpackPlugin(),
    isEnvProduction &&
      new MiniCssExtractPlugin(),
    useTypeScript &&
      new ForkTsCheckerWebpackPlugin(),
  ].filter(Boolean),
}
Copy code

In the above code, the MiniCssExtractPlugin plug-in can be added only when , isEnvProduction , is , true; When "isEnvProduction" is "false", then "false" will be added to the array, so the "filter" filter is used finally

4. Deconstruction assignment

Deconstruction assignment is very convenient and is often used in projects. It can be divided into the following two scenarios:

  • Deconstruction of objects / arrays;
  • Function parameter deconstruction;

Here are four common techniques.

1) Deep deconstruction

Most of the time, we only deconstruct one layer, but in fact, the deconstruction assignment can be deeply deconstructed:

let obj = {
  name: "dby",
  a: {
    b: 1
  }
}
const { a: { b } } = obj;
console.log(b); // 1
 Copy code

2) Use alias when deconstructing

If the object key name returned by the back end is not what we want, you can use the alias:

const obj = {
  // This key name is too long. We hope to replace it
  aaa_bbb_ccc: {
    name: "dby",
    age: 12,
    sex: true
  }
}
const { aaa_bbb_ccc: user } = obj;
console.log(user); // { name: "dby", age: 12, sex: true }
Copy code

3) Use default values when deconstructing

The default value can be used for object deconstruction. The condition for the default value to take effect is that the attribute value of the object is strictly equal to {undefined:

fetchUserInfo()
  .then(({ aaa_bbb_ccc: user = {} }) => {
    // ...
  })
Copy code

The above three features can be used in combination

4) Use short circuit to avoid error reporting

Although the deconstruction assignment is easy to use, it should be noted that the deconstructed object cannot be "undefined" or null, otherwise an error will be reported. Therefore, a default value should be given to the deconstructed object:

const {a,b,c,d,e} = obj || {};
Copy code

5. Expand operator

Use the expand operator to merge two arrays or two objects:

const a = [1,2,3];
const b = [1,5,6];
// bad
const c = a.concat(b);
// good
const c = [...new Set([...a,...b])];

const obj1 = { a:1 };
const obj2 = { b:1 };
// bad
const obj = Object.assign({}, obj1, obj2);
// good
const obj = { ...obj1, ...obj2 };
Copy code

Here we should pay attention to a problem. Although object and array merging seem to be But there are differences.

ES2015 extension operator only specifies that it can be used in arrays and function parameters, but it does not specify that it can be used in objects, and it is based on {for Therefore, only arrays, strings, sets, maps and other iteratable objects can be expanded. If ordinary objects are expanded to arrays, an error will be reported.

Objects In fact, it is the object expansion syntax in ES2018, which is equivalent to {object assign :

babeljs.io/docs/en/bab...

6. Check whether the attribute exists in the object

You can use the in keyword to check whether a property exists in an object:

const person = { name: "dby", salary: 1000 };
console.log('salary' in person); // true
console.log('age' in person); // false
 Copy code

However, the # keyword is actually not safe. It will also in clude the attributes on the prototype, such as:

"hasOwnProperty" in {}; // true
"toString" in {}; // true
 Copy code

Therefore, the following methods are recommended for judgment:

Object.prototype.hasOwnProperty.call(person, "salary"); // true
 Copy code

However, the above problem is too long. It's troublesome to write it every time. ECMA has a new proposal Hasown(), equivalent to {object prototype. hasOwnProperty. Alias of call():

Object.hasOwn(person, "salary"); // true
 Copy code

developer.mozilla.org/en-US/docs/...

It should be noted that this syntax has compatibility problems (chrome > 92), but it can be used safely as long as polyfill is configured correctly.

7. Object traversal

Many colleagues in the project will write this:

for (let key in obj) {
  if (Object.prototype.hasOwnProperty.call(obj, key)) {
    // ...
  }
}
Copy code

Tucao: make complaints about Object.keys # or # object When entries is converted into an array, it can be traversed by the array method, and it traverses its own attributes and will not traverse the prototype.

Object.keys(obj).forEach(key => {
  // ...
})

Object.entries(obj).forEach(([key, val]) => {
  // ...
})
Copy code

Retort: sometimes you don't want to traverse the whole object. Array methods can't use {break} to terminate the loop.

Tucao: it seems that you make complaints about array methods still not thorough enough. If you use find method to find eligible items, you will not continue to traverse them.

Object.keys(obj).find(key => key === "name");
Copy code

Summary: try not to write for loops in development, regardless of arrays and objects. Object is passed through object keys ,Object.values ,Object.entries is converted to an array for traversal. In this way, you can write JS expressions and make full use of functional programming.

8. Use includes to simplify if judgment

Such codes are often seen in projects:

if (a === 1 || a === 2 || a === 3 || a === 4) {
    // ...
}
Copy code

You can use include to simplify your code:

if ([1, 2, 3, 4].includes(a)) {
    // ...
}
Copy code

9. Promise two tips

1) async/await elegant exception handling

In the , async , function, as long as one of the , Promise , reports an error, the execution of the whole , async , function will be interrupted. Therefore, exception handling is very important. But in fact, the exception handling of the {async} function is very troublesome, and many colleagues are unwilling to write it. Is there a simple way? You can see an {async to JS} npm package, which can handle the exceptions of} async} function gracefully without adding} try manually Catch exception:

import to from 'await-to-js';

async function asyncFunctionWithThrow() {
  const [err, user] = await to(UserModel.findById(1));
  if (!user) throw new Error('User not found');
}
Copy code

In fact, the {Promise} returned before {await} encapsulates a layer to handle exceptions in advance. The source code is very simple. I have also realized the following:

function to(awaited) {
  // Whether Promise or not, it will be changed to Promise
  const p = Promise.resolve(awaited);
  // Await to JS uses then Catch usage
  // But in fact, the first callback function of the then method does not contain the code that will throw an exception
  // Therefore, using the then method and the second callback function to catch exceptions, no additional catch is required
  return p.then(
    res => {
      return [null, res];
    },
    err => {
      return [err, undefined];
    }
  )
}
Copy code

2) Promise as state machine

I saw a colleague write such code:

function validateUserInfo(user) {
  if (!userList.find(({ id }) => id === user.id)) {
    return {
      code: -1,
      message: "User not registered"
    }
  }
  if (
    !userList.find(
      ({ username, password }) =>
        username === user.username &&
        password === user.password
    )
  ) {
    return {
      code: -1,
      message: "Wrong user name or password"
    }
  }
  return {
    code: 0,
    message: "Login succeeded"
  }
}
Copy code

It is found that there are actually two states here, and then a field is needed to prompt the operation result. In this case, we can use {Promise. Some people say why, there is no asynchronous logic. We know that Promise # is actually a state machine. Even if we don't need to process asynchronous logic, we can use the features of the state machine:

function validateUserInfo(user) {
  if (!userList.find(({ id }) => id === user.id)) {
    return Promise.reject("User not registered");
  }
  if (
    !userList.find(
      ({ username, password }) =>
        username === user.username &&
        password === user.password
    )
  ) {
    return Promise.reject("Wrong user name or password");
  }
  return Promise.resolve("Login succeeded");
}

// Use the following
validateUserInfo(userInfo)
  .then(res => {
    message.success(res);
  })
  .catch(err => {
    message.error(err);
  })
Copy code

Obviously, the code becomes very elegant, but it can be more elegant. We know that the return value of the {async} function is a Promise instance, so the following two functions are equivalent:

// The normal function returns a promsie Resolve the value of the package
const request = (x) => Promise.resolve(x);

// The async function returns a value
const request = async (x) => x;
Copy code

Since the last , Promise is returned, why not add the , async , modifier directly in front of the function. In this way, successful results can be returned directly without {Promise Resolve} package:

async function validateUserInfo(user) {
  if (!userList.find(({ id }) => id === user.id)) {
    return Promise.reject("User not registered");
  }
  if (
    !userList.find(
      ({ username, password }) =>
        username === user.username &&
        password === user.password
    )
  ) {
    return Promise.reject("Wrong user name or password");
  }
  return "Login succeeded";
}
Copy code

Students who are not familiar with the {async} function can refer to Ruan Yifeng ES6 tutorial

Further, because throwing an exception inside , Promise , is equivalent to being , rejected, we can use the , throw , statement instead of , Promise reject() :

async function validateUserInfo(user) {
  if (!userList.find(({ id }) => id === user.id)) {
    throw "User not registered";
  }
  if (
    !userList.find(
      ({ username, password }) =>
        username === user.username &&
        password === user.password
    )
  ) {
    throw "Wrong user name or password";
  }
  return "Login succeeded";
}
Copy code

For the usage of throw statement, refer to MDN document

10. The string is less than two zeros

This requirement is common in development. For example, the date obtained by calling the Date api may have only one bit:

let date = new Date().getDate(); // 3
 Copy code

General practice:

if (data.toString().length == 1) {
    date = `0${date}`;
}
Copy code

Use string prototype. slice :

// No matter how many bits, splice a 0 in front, and then intercept the last two bits
date = `0${date}`.slice(-2);
Copy code

Use string prototype. padStart :

// When the length of the string is less than the value of the first parameter, the second parameter is added in front of it
date = `${date}`.padStart(2, 0);
Copy code

I hope it can help you!

For those who need to get free materials, add a little assistant vx: soxwv # to get materials for free!

Topics: Java Back-end Programmer architecture