How exactly do you write an elegant function? Come on, look here!

75183ba9c1f3269a733f6edca6c5666d.png

How exactly do you write an elegant function? Come on, look here!

Column introduction

For programmers, I believe that everyone has had the experience of modifying other people's code. Every time I receive such a task, I feel bitter in my heart, so I will bite the bullet, but look at it. When there are no comments and there are hundreds of lines of code for a function, my heart tends to collapse, and I think it's better to rewrite it myself.

The reason for this is that on the one hand, it may be unfamiliar with the original business logic. On the other hand, it is actually more because the previous code is too bad and the business logic is unfamiliar. We are looking for products and colleagues. By the way, it will be clear after sorting it out, but too bad code will increase our workload, and after the modification, I am still worried about 10,000 in my heart, for fear of fixing new problems.

Therefore, how to write more elegant and maintainable code becomes very important. If you want to make your code more elegant and tidy, you must develop good habits in terms of naming, functions, comments, formats, etc. Therefore, the clean code of this column - theory and practice is to summarize from theory to practice from the aspects of naming, functions, comments, etc., hoping to give everyone a clearer understanding.

Here are two things to emphasize:

  1. The code we write is for people, for us programmers to see, not for machines . Therefore, when we write code, we must always think about it. If others read the code we wrote at this time Is it possible to understand the meaning of the code more clearly, if it is not easy to understand, does it mean that our code can be further optimized, does it mean that we need to add some comments? In short, it is all for the code to be understood by others.

  2. Whether the code is well written is not positively related to the technical ability itself . That is to say, to write the code well, it is more necessary to develop good habits and pay attention to this matter from the attitude level, which is not strongly related to the technical ability itself. Of course, the technical ability itself is also very important. It can be added so that our code can use better design patterns to organize the code.

Dachang Technology Advanced Front-end Node Advanced

Click on the top programmer's growth guide, pay attention to the public number

Reply 1, join the advanced Node exchange group

foreword

In the process of project development, we will definitely write various functions. When it comes to functions, the first thing that comes to mind may be: 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. This is what needs to be discussed in this section.

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

  1. How to define the function name to be more standardized?

  2. The most suitable function parameters are passed. Is there any requirement for the order of parameters? Or just write it casually, it doesn't matter which one comes first and which comes after?

  3. How many lines of code should be written in the function body? How long can you write your own functions at most?

  4. When should the return value be added? In the end what kind of function needs to return data? What kind of function does not need to return data?

The reason why I ask these questions is mainly to emphasize that to really write an elegant function, there are actually many details that need to be paid attention to, and many details are points of attention that we may never think about when we usually write, at least I When I first started writing code, it was like this, hahaha.

The old rules continue to be elaborated from the following three aspects:

  • Theory

  • Specifications

  • Actual combat

Theory

just do one thing

As the name implies, we need to ensure that our function is split very clearly 每个函数都只做一件事儿. When we find that the function is getting bigger and bigger, we need to consider whether we can further split into multiple sub-functions, so as to ensure that each of our functions implements Functions only do one thing, so the function will be more concise and pure.

No side effects

When it comes to function side effects, you may think of pure functions in functional programming, that is to ensure that the same input has the same output every time, without any side effects. Pure functions are beautiful, and we don’t have to worry about other intentions. Unexpected results appeared.

But when we usually use vue, react and other frameworks to develop, it is impossible and impossible to use pure functions completely. However, we can continue this idea into our usual code, that is, we must ensure that a function is pure as much as possible. The pure here does not refer to pure functions, but to only do one thing and reduce side effects as much as possible. .

For example, when we write a function to read a file, the normal idea is three steps: read-data format conversion-output. But we read the database again in this method. Obviously, the function of this function is not only one thing.

Clear function scenarios

What is a function scene? In fact, to put it bluntly, what do you use functions to do? From our usual development, nothing more than the following two situations:

  1. To perform an operation: it may be the execution of several actions in succession.

  2. To get data: Typically, a request is sent from the backend to get data.

If you still don't understand it, let's analyze it from the perspective of parameters and return values:

  1. No parameters, no return value

  2. No parameters, return value

  3. With parameters, no return value

  4. parameter, return value

Let's finally combine the two to see:

  1. If it is to perform a certain operation, there is generally no return value, and the parameters may or may not be present, depending on whether the operation depends on other data.

  2. If it is to get data: generally there is a return value, and the parameters may or may not be.

Why say these things? In fact, when we write a function, we need to be clear about 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) {
    console.log('true')
    return true;
  } else {
    console.log('false')
    return false;
  }
}
let person = {};
person.set('name', 'kobe');
person.set('age', 41)
复制代码

What's wrong with the code above? As soon as we see the set function, we will feel that the general function of the function is to set new attributes and values ​​for a certain data. Normally, there is no return value. As a result, we found that there is still a part of the code in the function body. Check the age, there is a return value. Obviously, this code makes two mistakes:

  1. no one thing

  2. In the scenario without a clear function, the normal logical set function generally does not have a return value, but here it returns true/false, which is very confusing.

How to modify it?

  1. According to the idea that the function only does one thing, we need to extract the logic of checking the age into a function.

  2. Modify the function name to ensure that its name is consistent with its internal implementation.

function setAndCheckAge(attr, val) {
  this[attr] = val;
  return checkAge();
}
function checkAge(age) {
  if (age > 30) {
    console.log('true')
    return true;
  } else {
    console.log('false')
    return false;
  }
}
let person = {};
person.set('name', 'kobe');
person.set('age', 41)
复制代码

Note: You don't need to pay too much attention to whether the actual logic of the above code is reasonable. For example, how to check the age in the set method? Yes, in actual development, there is probably no such business logic. explain his thoughts.

function parameter

最理想的情况是参数是零(零参数函数),其次是一(一参数函数),再次是二(二参数函数),应该尽量避免三(三参数函数),必须有足够的理由才可以使用三或者三个以上的参数。

Because the more parameters there are, the more various combinations there are, which means that the logic inside the function will be more complicated.

For function parameters, the following ideas are summarized:

Reduce the number of function parameters as much as possible. If there are multiple parameters, it involves the order of the parameters, and we need to consider which parameters should be placed in the front and which parameters should be placed in the back. If there are really too many parameters, it is necessary to consider whether the parameters of the same type can be encapsulated into a parameter object.

don't repeat yourself

That is, we must ensure the reusability of the code, and the function is the most important thing. If there are some public functions, we must abstract them separately. Never repeat functions that define the same functionality.

Specifications

In the specification, we will explain from the following aspects:

  • function declaration

  • function parameter

  • function call

  • arrow function

function declaration

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

Reason: The way of using function declaration will have life promotion, that is to say, calling before the function declaration will not report an error. Although it can run successfully from the syntax level, from the perspective of code readability and maintainability, such logic is obviously not in line with normal thinking, that is, the logic of declaring first and then calling.

// bad case
function foo() {
  // ...
}

// good case 
const foo = () => {
  // ...
}
复制代码

[MUST] Wrap the immediately executed function in parentheses.

Reason: mainly from the perspective of code readability, the immediate function call belongs to a relatively independent unit, and the outside is uniformly wrapped with a layer of parentheses, which is more clear.

// bad case
(function() {
  // ...
})();

// good case
(function() {
  // ...
}())
复制代码

[MUST] Remember not to declare functions ( if, while, etc.) in non-function blocks.

// bad case
if (flag) {
  function foo() {
    console.log('foo')
  } 
}

// good case
let foo;
if (flag) {
  foo = () => {
    console.log('foo')
  }
}
复制代码

[Recommended] Never use the function constructor to create a new function.

// bad case
let foo = new Function('a', 'b', 'return a + b');
复制代码

[Required] A space is required in the function declaration statement

// bad case 
const a = function(){};
const b = function (){};
const c = function() {};
function d () {
  // ...
} 

// good
const a = function () {};
const b = function a() {};
function c() {
  // ...
}
复制代码

function parameter

[MUST] Never name a parameter arguments. This will override the function's default argumentsobject .

// bad case
function(arguments) {
  // ...
}

// good case
function(args) {
  // ...
}
复制代码

[Recommended] Use rest syntax ...insteadarguments

Here, it is mainly to explain how to obtain the parameters of arguments.

  1. Array.prototype.slice.call(arguments)

  2. ...arguments 【Recommended】

// bad case
function foo() {
  const args = Array.prototype.slice.call(arguments);
  return args.join('');
}

// good case
function foo(...args) {
  return args.join('');
}
复制代码

[Recommended] Use the default parameter syntax instead of changing function parameters.

The main purpose here is to explain how to set default values ​​for parameters. There are actually many methods:

  1. Determine whether the parameter is empty, and then manually assign a default value

  2. Use default value syntax [recommended]

// bad case
function foo(options) {
  if (!options) {
    options = {};
  }
}
// bad case
function foo(options) {
  options = options || {};
  // ...
}

// good case
function foo(options = {}) {
  // ...
}
复制代码

But be careful: when setting default values, be sure to avoid side effects. E.g:

let opts = {};
function foo(options = opts) {
  // ...
}
opts.name = 'kobe';
opts.age = 41;
复制代码

Explanation: The above case means that although the parameter default value is used, the default value refers to an external reference object. Obviously, this has side effects, because the external object may change at any time. Once changed, it will cause our default value to change as well. So these spellings are problematic, avoid using them!

[Recommended] Always put default parameters last.

// bad case
function foo(options = {}, name) {
  // ...
}

// good case
function foo(name, options = {}) {
  // ...
}
复制代码

[Recommended] Do not change the input parameters, and do not reassign the parameters. eslint:no-param-reassign

Reason: After we pass a variable into the function as a parameter, if the variable is reassigned or modified inside the function, it will directly cause the variable to change. If the variable is referenced elsewhere, it is likely to cause unexpected The problem. (Note: The variables here mainly refer to the reference data type, because the basic data type will be copied directly when it is used as a function parameter)

// bad case
function foo(a) {
  a = 1;
}

// good case
function foo(a) {
  let b = a || 1;
}
复制代码

Note: When we call, we are not sure whether the incoming a is a reference data type or a basic data type, so it is always required not to modify the input parameters, but there may be a question at this time? Because there are still quite a few scenarios for modifying input parameters in js, the typical ones are: traversing a list, manually adding indexes or flags, etc. For example: the following code:

const list = [];
list.forEach((item, index) => {
  item.index = index;
  item.isShow = index > 2;
})
复制代码

The above code is actually quite common. If this happens, eslint will prompt no-param-reassign. How to solve it?

  1. Turn off the rule check in the current code. Note: it is not a global shutdown, because in most scenarios, it is not recommended to modify the input parameters.

  2. Using deep clone, first copy an item, modify it, and then return the new item.

function call

[Recommended] Use the spread operator first ...to call variadic functions

// good case
console.log(...[1, 2, 3, 4]);
复制代码

arrow function

[Recommended] When you must use anonymous functions (when passing inline functions), use arrow 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;
});
复制代码

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

// bad case
[1, 2, 3].map((item) => item + 1);

// good case
[1, 2, 3].map(item => item + 1);
复制代码

[Recommended] When the function body is composed of an expression without side effects, delete the curly braces and return, otherwise keep it.

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

// good case
[1, 2, 3].map(item => item + 1)
复制代码

At the same time, it should also be noted that if the expression contains comparison operators such as >=, <=, it is recommended to use parentheses to isolate them, because they are easily confused with the arrow function symbol =>.

// bad case 
[1, 2, 3].map(item => item >= 1 ? 1 : 0)

// good case
[1, 2, 3].map(item => (item >= 1 ? 1 : 0));
复制代码

Actual combat

Through the theoretical chapters and the specification chapters, we have basically learned what to pay attention to when writing a function. One of the points is very important: 明确函数场景, in other words, it is clear when the function should have parameters and when should it return value?

In response to this, I will emphasize it again, because when writing code, I do write a lot of functions, and I also encounter many functions that do not seem to be particularly clean and clear. Here we will start from a practical example and further explain:

For example: we want to get table data from the backend and render it, but the data returned by the backend does not conform to the format of the table and needs to be converted manually, so we can easily write the following code:

let rawData = [];
let tableData = [];
const transformRawData = () => {
    tableData = rawData.map(item => {
        // 经过一系列处理...
    });
}
const render = () => {
  rawData = await fetchRawData();
  transformRawData();
}
复制代码

What's wrong with the above code? The problem still lies in the transformRawData method. As the name suggests, the main function of this method is to transform 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 also be a return value. . And we do not deal with this now, but directly rely on global variables and directly convert.

Although there is no problem in function, it can be further optimized from the code level.

let rawData = [];
let tableData = [];
const transformRawData = (data) => {
    return data.map(item => {
        // 经过一系列处理...
    });
}
const render = async () => {
  rawData = await fetchRawData();
  tableData = transformRawData(rawData);
}
复制代码

After changing this, transformRawData becomes a more pure function. Without relying on global variables, its function is to convert the data format of the incoming parameters, and after the conversion, return new data.

Summarize

I believe that through the study of this section, everyone has a further understanding of how to write functions. Finally, we are emphasizing two points:

Ensure that the function only does one thing, reduce its side effects and clarify the usage scenarios of the function [Note: this is actually a common mistake when writing code]

At the same time, combined with formatting tools such as aslant and prettier, the format of the function definition will be further verified. I hope everyone can write functions better and better in 2022. Come on!

  • Author: Silent Lyricist

  • Link to this article: https://juejin.cn/post/7061842017487159333

Node 社群


我组建了一个氛围特别好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你对Node.js学习感兴趣的话(后续有计划也可以),我们可以一起进行Node.js相关的交流、学习、共建。下方加 考拉 好友回复「Node」即可。

如果你觉得这篇内容对你有帮助,我想请你帮我2个小忙:

1. 点个「在看」,让更多人也能看到这篇文章2. 订阅官方博客 www.inode.club 让我们一起成长

点赞和在看就是最大的支持❤️

Guess you like

Origin blog.csdn.net/xgangzai/article/details/123650121