Blink of an eye, is the last day of the holiday, everyone at home to play happy? As for me, it is the first time to spend the Spring Festival outside, so I am bored. After the holiday, I want to officially enter the New Year’s journey, which is looking forward to but do not want to go to work. Ha ha ha, it is better to continue to write the article, dang dang dang, the third chapter: [code clean way] function chapter came.

Column introduces

For programmers, I believe you ever have such experience, to change other people’s code, got such a task each time, the in the mind has a bitter could not say ah, so crustily skin of head, but there is no any comments, see a function hundreds of lines of code, the heart is more tend to collapse, thought also is to rewrite it again?

The reasons that, on the one hand, because is not familiar with the business logic might be in the original, on the other hand, actually more because the code before writing is too bad, not familiar with the business logic, we find products, looking for a colleague on the comb will be clear, but it was too bad code will cost increase our workload, and after you have modified, Heart or ten thousand not at ease, for fear of changing a new problem.

Therefore, how to write a more elegant, more maintainable code, becomes very important, if you want to make your code more elegant and tidy, from the name, function, annotations, format, and other aspects to form a good habit, therefore, this column code tidy way – the theory and practice Is from the name, function, comments from theory to actual combat summarizes many ways, such as, Hopefully we can have a clearer understanding.

Two words should be emphasized here:

  1. We write code is to the people, is to see our program apes, not to show the machine, therefore, when we write code to often think, we write the code, if the others look at whether can more clearly understand the meaning of the code, if you feel not so good understanding, does that mean we can further optimize the code, Does that mean we need to add some comments? Anyway, it’s all about making the code readable.
  2. Written code is good, and the technical ability itself is not a positive correlation, that is to say, it will write good code, more is to form a good habit, and from the aspects of attitude to attach importance to this matter, and the technical ability itself has no strong correlation, the relationship between the technical ability itself is also very important, of course, it can addition to our code can be used to better design pattern to organize the code.

The column appears below:

  1. Clean code: Named
  2. Comments on code cleanliness
  3. [Code clean way] function
  4. [Clean Code] Format (to be released)
  5. Objects and Data Structures (to be released)
  6. Error Handling (to be released)
  7. Unit Testing for Clean Code (to be released)
  8. Iteration (to be released)

Note: This column will continue to focus on code cleanliness in more depth in the future. Please like and follow 😊😊😊

preface

We usually write a variety of functions in the process of project development. When it comes to functions, the first thing that may come to mind is: function name, function parameters, function body and function return value. It is true that the function basically contains the above four parts, but each part actually contains a lot of details that we need to pay attention to, and that is what this section will discuss.

Before you do that, first think about whether you have thought about the following questions when writing functions:

  1. How to define a function name?
  2. How many parameters to pass the function is the most appropriate, parameters before and after the order is required? Or is it just random, which comes first and which comes last?
  3. How many lines of code are appropriate for the function body? How long can I write a function?
  4. When should I add the return value? What kind of function needs to return data? What function does not need to return data?

The main reason I’m asking these questions is to emphasize that in order to really write an elegant function, there are a lot of details that you have to pay attention to, and a lot of details that you probably never think about when you’re writing, at least when I started writing code, hahaha.

That old rule, continue to elaborate from the following three aspects:

  • Theory of article
  • Standard article
  • Practical article

Theory of article

Just do one thing

As the name suggests, we must ensure that our function function is split very clear, do one thing every function, when found that the function is more and more big, we need to consider whether can split out further more child function, the function of the implementation of each function to ensure that we are only do one thing, this function will be more simple and pure.

No side effect

When it comes to functional side effects, you may think of pure functions in functional programming, that is, the same input is guaranteed to have the same output every time, without any side effects. Pure functions are nice, and we don’t have to worry about other unexpected results.

However, when we use vUE, React and other frameworks to develop, it is impossible and impossible to use pure functions completely. But the idea that we can carry over to our regular code is that we want to make sure that a function is as pure as possible, and by pure WE don’t mean pure functions, we just do one thing, with as few side effects as possible.

For example, if we write a function to read a file, the normal idea is three steps: read – data format conversion – output. But as we read the database in this method, it is clear that this function does more than one thing.

Explicit function scenarios

What are the scenarios of functions? So basically, what do you do with a function? From our usual development, there are no more than the following two situations:

  1. To perform an action: perhaps the execution of several actions in succession.
  2. Get the data: Typically, send a request from the back end to get the data.

If that doesn’t make sense, let’s look at it in terms of parameters and return values:

  1. No parameter, no return value
  2. No argument, return value
  3. Argument, no return value
  4. There are parameters, there are return values

Let’s finally put the two together:

  1. If you are performing an operation, there is usually no return value. The parameter may or may not have a value, depending on whether the operation depends on other data.
  2. If it is to fetch data: usually there is a return value, the parameter may or may not have a value.

Why do you say this? In fact, when we write a function, we should make it clear which scenario it belongs to, and do not mix it, for example: the following function

function set(attr, val) {
  this[attr] = val;
  if (this['age'] > 30) {
    return true;
  } else {
    return false;
  }
}
let person = {};
person.set('name', 'kobe');
person.set('age', 41)
Copy the code

What’s wrong with the code above? When we look at the set function, we assume that the function’s general function is to set new properties and values for some data. Normally, there is no return value. Instead, we find that the body of the function is also a part of the code that checks the age and returns a value. Obviously, this code makes two errors:

  1. There’s no one thing to do
  2. In the absence of an explicit function, the normal logical set function does not normally return a value, but it returns true/false, which is confusing.

So how do you fix it?

  1. With the idea that the function only does one thing, we need to separate the age-checking logic into a function.
  2. Change the name of a function to ensure that it is consistent with its internal implementation.
function setAndCheckAge(attr, val) {
  this[attr] = val;
  return checkAge();
}
function checkAge(age) {
  if (age > 30) {
    return true;
  } else {
    return false;
  }
}
let person = {};
person.set('name', 'kobe');
person.set('age', 41)
Copy the code

Note: the above code you can not care too much about the actual logic is reasonable, such as: how to verify the age in the set method, yes, the actual development, most likely will not have such business logic, here is just to illustrate the idea.

Function parameters

Ideally, the parameters are zero (zero-argument function), followed by one (one-argument function), and then two (two-argument function). Three (three-argument function) should be avoided as much as possible, and there must be a good reason to use three or more parameters.

The more arguments there are, the more combinations there are, which means the more complex the logic inside the function.

For function parameters, the following ideas are summarized:

  1. Reduce the number of function arguments as much as possible.
  2. If there are multiple arguments, then the order of the arguments is involved, and we need to think about which arguments should come first and which ones should come after.
  3. If there are really too many arguments, consider whether you can encapsulate arguments of the same type into a parameter object.

Don’t repeat yourself

That is, we must make sure that the code is reusable, functions are the most important, and if there are some common functions, we must isolate them. Never define functions that do the same thing twice.

Standard article

In the specification part, we explain it from the following aspects:

  • Function declaration
  • Function parameters
  • A function call
  • Arrow function

Function declaration

[Optional] Use named function expressions instead of function declarations eslint:func-style

Reason: There is a life boost in using function declarations, which means that no error is reported when called before a function is declared. Although it works syntactically, it doesn’t work in terms of code readability and maintainability.

// bad case
function foo() {
  // ...
}
​
// good case 
const foo = () => {
  // ...
}
Copy the code

“Must”Enclose the immediate function in parentheses.

Reason: Mainly from the point of view of code readability, function immediate call belongs to a relatively independent unit, unified with a layer of parentheses wrapped outside, more clear.

// bad case (function() { // ... }) (); // good case (function() { // ... } ())Copy the code

“Must”Remember not to declare functions in non-functional blocks (if.while, etc.).

// bad case
if (flag) {
  function foo() {
    console.log('foo')
  } 
}
​
// good case
let foo;
if (flag) {
  foo = () => {
    console.log('foo')
  }
}
Copy the code

“Recommended”Never use a function constructor to create a new function.

// bad case
let foo = new Function('a', 'b', 'return a + b');
Copy the code

“Must”Function declaration statements require whitespace

// bad case 
const a = function(){};
const b = function (){};
const c = function() {};
function d () {
  // ...
} 
​
// good
const a = function () {};
const b = function a() {};
function c() {
  // ...
}
Copy the code

Function parameters

“Must”Never name a parameter asarguments. This will override the function defaultargumentsObject.

// bad case
function(arguments) {
  // ...
}
​
// good case
function(args) {
  // ...
}
Copy the code

“Recommended”Using rest syntax.Instead ofarguments

Argumentargument.getarguments ();

  1. Array.prototype.slice.call(arguments)
  2. . [详 细]
// bad case function foo() { const args = Array.prototype.slice.call(arguments); return args.join(''); } // good case function foo(... args) { return args.join(''); }Copy the code

“Recommended”Use the default argument syntax instead of changing function arguments.

There are several ways to set the default value of a parameter:

  1. Determine if the parameter is null and manually assign a default value
  2. Use default syntax
// bad case function foo(options) { if (! options) { options = {}; } } // bad case function foo(options) { options = options || {}; / /... } // good case function foo(options = {}) { // ... }Copy the code

A word of caution: when setting defaults, avoid side effects. Such as:

let opts = {};
function foo(options = opts) {
  // ...
}
opts.name = 'kobe';
opts.age = 41;
Copy the code

Note: In this case, the default value of the parameter is used, but the default value is a reference to an external object, obviously, there are side effects, because the external object can change at any time. Any change will cause our default values to change. So these are problematic, avoid them!

“Recommended”Always put default parameters last.

// bad case
function foo(options = {}, name) {
  // ...
}
​
// good case
function foo(name, options = {}) {
  // ...
}
Copy the code

“Recommended”Don’t change input arguments or reassign parameters. Eslint:no-param-reassign

Reason: When we pass a variable as a parameter to a function, if the variable is reassigned or modified inside the function, it will directly cause the variable to change, and if the variable is referenced elsewhere, it may cause unexpected problems. (Note: variables here refer mainly to reference data types, because the underlying data types are directly copied as function parameters.)

// bad case
function foo(a) {
  a = 1;
}
​
// good case
function foo(a) {
  let b = a || 1;
}
Copy the code

Note: We are not sure whether a is a reference data type or a basic data type when we call it, so we will not modify the input parameter. Because in the JS modification into the scene or a lot of parameters, the typical is: traversal a list, manually add index or identifier bit, etc.. For example: the following code:

const list = [];
list.forEach((item, index) => {
  item.index = index;
  item.isShow = index > 2 ? true : false;
})
Copy the code

The above code is actually quite common; if this happens, ESLint will prompt no-param-reassign. How do you solve it?

  1. To turn this rule validation off in the current code, note: not globally, because in most scenarios, modifying the input parameter is still not recommended.
  2. With deep cloning, copy an item, modify it, and return the new item.

A function call

“Recommended”Extended operators are preferred.To call a variable argument function

// good case console.log(... [1, 2, 3, 4]);Copy the code

Arrow function

“Recommended”Use arrow functions when you must use anonymous functions (when passing inline functions).

// bad case
[1, 2, 3].map(function (x) {
  const y = x + 1;
  return x * y;
});
​
// good case
[1, 2, 3].map((x) => {
  const y = x + 1;
  return x * y;
});
Copy the code

[Recommendation] Only one parameter can be used without parentheses, and more than one parameter can be used with parentheses

// bad case
[1, 2, 3].map((item) => item + 1);
​
// good case
[1, 2, 3].map(item => item + 1);
Copy the code

[Recommendation] If the function body consists of an expression with no side effects, remove the braces and return. Otherwise, keep the braces.

// bad case
[1, 2, 3].map(item => {
  return item + 1;
})
​
// good case
[1, 2, 3].map(item => item + 1)
Copy the code

Also note that parentheses are recommended for expressions that contain comparison operators such as >=,<=, etc., because they are easily confused with the arrow function symbol =>.

// bad case 
[1, 2, 3].map(item => item >= 1 ? true : false)
​
// good case
[1, 2, 3].map(item => (item >= 1 ? true : false));
Copy the code

Practical article

From the theory and specification papers, we have basically learned what needs to be paid attention to when writing a function. One of the important points is to clarify the function scenario. In other words, to clarify when a function should have parameters and when it should have return values.

For this point, I would like to emphasize more, because usually when writing code, I do write a lot of functions, but also encountered a lot of functions that do not look particularly clean and clear, here we start from a practical example to further illustrate:

For example, if we want to fetch the table data from the back end and render it, but the data returned by the back end does not fit the table format and needs to be converted manually, we can easily write code like this:

const rawData = []; const tableData = []; Const transformRawData = () => {tableData = rawdata.map (item => {// after a series of processing... }); } const render = () => { rawData = await fetchRawData(); transformRawData(); }Copy the code

What’s wrong with the above code? The problem is that the method transformRawData, as its name implies, transforms data, which means that there should be an input parameter, and the result of the transformation should also be reflected in the return value, so there should be a return value. Instead of doing this, we rely directly on global variables and convert directly.

While there are no functional issues, they can be further optimized at the code level.

const rawData = []; const tableData = []; Const transformRawData = (data) => {return data.map(item => {// After a series of processing... }); } const render = async () => { rawData = await fetchRawData(); tableData = transformRawData(rawData); }Copy the code

So transformRawData becomes a much purer function. It does not depend on the global variable. Its function is to convert the data format of the passed parameter and return the new data after the conversion.

conclusion

I believe that through the learning of this section, you have a further understanding of how to write functions. Finally, we emphasize two points:

  1. Make sure the function does only one thing and reduce its side effects
  2. Make it clear how the function will be used.

At the same time, aslant, Prettier and other formatting tools are used to further verify the format of function definition. I hope we can write the function better and better in 2022, let’s go!

Writing is not easy, welcome to praise comments oh, have questions can feedback at any time! 😊 😊 😊