In this article I will explain how to write high-quality functions in terms of their naming, annotation, and robustness.
PS: This article is much simpler than the previous one.
The introduction
Before writing the second part, let me say one thing. In view of the comments of my previous article on how to write high-quality functions – Knock mountain shake tiger, some questions raised by my partners, I also read all of them, hereby write an answer.
The q&A is posted on issues, click the following link to TP:
How to write high quality functions — The shock of the q&A
If you have any questions about my answer, please discuss issues and this article will not be on the Nuggets.
PS: mention some public number (Denver, strange dance weekly, etc.), because the article at the beginning there is an error, then the public without synchronization, then I can’t help you to change, so it opens at the public article, looked at those mistakes, but helpless mood, silently ob: thanks for landlords, reprinted that so 😂.
Okay, without further ado, let’s just get started.
The function name
As you can see from the picture at the beginning of this article, naming and caching are two of the biggest problems in computer science.
And today is about function naming, although function naming involves a narrow range, but the idea is the same, can be borrowed from other forms.
I have read the variable chapter of the code corpus, and also read some source code, such as Lodash, Ramda these function tool libraries. Based on my personal insights, here are some best practices that I think can help you completely solve the naming problem.
PS: Although there is no best practice for variable naming, I personally think there is a perfect Best Pratice for front-end function naming.
What are the current problems with front-end function naming
What’s the problem? If you ask me, industry standards like the hump, uppercase classes and constructors, underscores, $, etc. are not bottlenecks. The real bottleneck is something you don’t know, or something you don’t know but can’t do. Such as:
- Differences between Chinese and English
- Do not know how to improve the accuracy of naming from multiple dimensions
- Can’t use assistive tools
The following is a brief and concise analysis.
PS: I won’t mention the familiar industry standards such as humps.
The differences between Chinese and English
Why do I say this in the first place? Because I think this is the biggest problem with naming right now. Good English level of the brothers are not many, many people can not master the English naming that set of grammar rules, to put it bluntly is that your English level can not reach as foreigners can write in line with the English style of the name.
Why can’t they be named in Chinese?
There are three reasons:
- The essential problem with pinyin: The existence of Chinese pinyin polysemy, think of trembling.
- Problems with Chinese characters: although the auxiliary tools have been very perfect, but can not be popularized, can not be in line with international standards, to put it in a word, too small, foreigners do not know, do not play with you.
- The chain of contempt has been formed (emmmm).
Difficulties in using English
The biggest difficulty is not.
React’s life cycle:
-
componentDidMount
-
componentWillReceiveProps
-
shouldComponentUpdate
Many people may wonder why did and will are used. Ok, remember to finish, and then over a period of time, the interview was asked, and then the heart OB: is componentMounted or what to…
What a vivid example, well, read on for a weird answer.
How to make naming have English style
Black face? How are we gonna do that?
Elder brother, read more high school or junior high school English grammar knowledge. Let me give you the simplest example, and you’ll get the idea.
ComponentDidMount is a hook for a life cycle like React, but why is it named that way?
Why did componentWillReceiveProps so named?
Here’s the answer:
Notice in the figure above did represents simple past and will represents simple future.
Then we refer to the simple past tense and the simple future tense, and look like this:
Simple past tense:
Simple future tense
Look at the red arrow in the image above. Did represents the past simple, and the time refers to the time at which the action took place. It is used here to highlight the hook, which is executed once the hook is mounted successfully. Did you get it all at once? Okay, will. Same thing.
Don’t say anything, go and have a good look at middle and high school English grammar.
Named by the result returned by the function
It’s a little feature like shouldComponentUpdate, why should be first.
Because this function returns a Boolean value. So we can say that this is a question, in the form of a question that tells us that there is some uncertainty here, and we need to update it based on the return value.
As for the English grammar of questions, we should read more grammar books.
With the help of tools
With Google Translate
Forget about Google Translate. Everyone knows it
With the help of codelf
This is a magic tool for searching variable names in various open source projects for your reference.
Address: unbug. Making. IO/codelf /
VSCODE plug-in for the corresponding name is also available, specific how to use, small partners to understand it.
How to avoid ambiguous and unreadable function naming
Maybe you give a function a name, and when other people see the function, they don’t know what the name is, so they have to guess.
For example, I found a function from the ramda source code that looks like this:
var forEachObjIndexed = _curry2(function forEachObjIndexed(fn, obj) {
var keyList = keys(obj);
var idx = 0;
while (idx < keyList.length) {
var key = keyList[idx];
fn(obj[key], key, obj);
idx += 1;
}
return obj;
});
export default forEachObjIndexed;
Copy the code
This function is called forEachObjIndexed. It’s not a good idea to look at the name of this function, but it’s not a good idea to look at it for the first time. It’s not a good idea to look at this function.
See, how detailed, of course, this is for output documentation, but it gives us a very good solution. That is:
If you really can’t think of a name, or if you already know that the name is bad, like too long, like hard to understand, then you don’t have to struggle. Write a name that you think is ok, and leave the rest of the time to your comments. For example, the annotation of the first part of Foreachob Indexed is the overall introduction and explanation of the whole function.
If you have a bad function name, it is very important to explain the function as a whole. You must do well in this part, if your English is not good, then honestly write in Chinese. So this part is done, and if you name this function two lines of text, or if you name it in Martian, it doesn’t matter, it doesn’t matter.
Classification of function naming
Finally, the classification of naming, which is my personal opinion.
The reason I mention the classification of function naming is because we often see functions named this way (common in source code). Such as:
- $xxx()
- _xxx()
Copy the code
This kind of function name with various prefixes doesn’t look good. This naming, in my personal look is very awkward, but why have this naming, in fact, this is the front end of the helpless move.
The core reason is that JS language does not support private variables, leading to the use of _ or $to ensure that the corresponding external invisible, by treating the symptoms rather than the root cause to solve this problem.
So I divided the front-end function names into two categories, as follows:
Type 1: functions that do not want to be exposed to external access (e.g. for internal use only)
Type 2: Functions exposed to external access (various functional methods)
My personal view at present, roughly also these two big categories.
PS: here I do not consider the function name of Symbol initialization, such as the following code:
const ADD = Symbol('add')
[ADD](a, b) {
console.log('a + b')}Copy the code
As for the use of Symbol, we can understand it by ourselves. I will not consider this special case.
PS: about the helpless, in the understanding of more time, will find that in the front, there is no method (design patterns, hack method) can absolutely solve the above problems, so sometimes you have to use, such as _ because when can solve this problem, the more simple way more popular, this is reality.
conclusion
To summarize the best practices:
Study more junior high school English grammar, the function in the open source project named not so hard to understand, through the study of grammar and use tools, function named basic can solve, if meet the purpose of can’t write clearly describes the function of naming, please be sure to write good annotation to the function, no matter how to understand how long the function name, as long as there is a good comment, That’s one thing that’s acceptable. After all, you do not want to name so difficult ah, but the ability is limited, then use Chinese to do a good note, the effect is also great.
I’ve already covered how to provide quality with good function naming, the answers are all in the text, how to use tools, how to understand the naming syntax in English, and how to increase the accuracy and readability of naming meanings through multi-dimensionality. Simple talk about the current front boundary function named classification, we experience and use it.
PS: SOME of the well-known points I will not say, such as verb + noun, noun + verb, hump, etc., clearly describe the purpose of the function, these are not pain points, PAIN points I have described, and best practices.
Function comments
Let’s talk about annotations for functions, which improve readability on the one hand, but also allow you to do other things with annotations, such as generating online documentation. Annotations are necessary for a high-quality function, but that doesn’t mean that all functions need annotations. Rich rich way of living, poor poor cool, important or complex functions, then give a good comment, simple or not important functions, you can not give a comment or give a simple comment. There’s no point in empty talk, so let’s take a look at some of the ways in which functions are commented today.
PS: Pay attention to the wording above, if you think this function is badly named, you should give it a good comment.
Let’s start with some comment styles for well-known NPM packages
Just as college students read a lot of literature before writing a paper, so do we. Let’s take a look at several well-known NPM packages that play with annotations.
Annotation style of egg.js
From the diagram, we can see the annotation of the entry file in egg.js, leaving aside the fact that this is a doc tool annotation rule (don’t worry about the details). Let’s take a look at the annotation features and see if they differ from the annotation style you have in mind. The simplicity and uncluttered nature of this entry file is a good idea to absorb and inspire when you do open source projects.
Continue with the image below:
This is a base class that has been abstracted to show the style of comments the author [Yiyu He] made when He wrote the class. What can we learn from this picture? There are the following:
First point: comment rules for constructors, comment rules for expression statements.
The second point: the choice of annotations, some variables can not be annotated, some to annotate, do not have that kind of annotation to all annotations thought.
Here are two more interesting pictures:
Look at the arrows in the two graphs above, which point to the same author [fengmk2]. Let’s look at his function annotation rules. For a taste of the difference, think about why the first image has no Spaces, the second has Spaces, and the comment on the returned this. For example, many people tend to comment this directly as Object.
lodash.js
You can’t talk about function comments without talking about Lodash.js. But write this, I found that this if add to go, the second text and over, that is no longer said here, we have a look at the source code analysis (this operation really sweet).
Thoughts on generating online documents through annotations
Some people say that comments should be very standard, easy to give others, such as using JSDoc. My personal view here is that jSDoc is not necessary for web projects that do not require open source, for the following reasons:
- Cumbersome, need to follow
jsdoc
The rules to - Personally,
jsdoc
It’s intrusive, and the rules of documentation need to be written in code.
Here I think if you want to write an annotation manual, for large projects I recommend using Apidoc because apidOC is not very intrusive and does not require rules to be written in code, you can write all the rules in one file. Specific use method, I did not say, oneself search relevant data.
But for small projects, there is no need to write a separate API document. If is a large open source project, you should consider what is more, the first thing you need to have the official website of the open source you will see some open source projects online website seems cool, in fact this world don’t need is a wheel, you can make this website soon, let’s look at how to do.
First of all, we take a look at the taro source code, and we will find the following figure:
Here is the secret to generating a static website, just execute the NPM Run docs. Using the Docusaurus package, you can do your own search if you don’t know.
And here you see the picture below:
As you can see from the figure, the content of the document comes from the Docs directory, which is full of MD files, and the documentation for open source projects is here.
Of course, there are also corresponding documents directly into the corresponding code directory, such as ant-Design as shown below:
Put the document directly in the component directory.
From here, we can know how the official website of the current popular open source project is implemented, and how to write the documentation. You can say that this has nothing to do with function comments, but it seems to have something to do with them.
My personal annotation habits
Here are some of my personal comments or comments on function annotations (function annotations only).
shareVSCode
A couple of tools for annotations
Better Comments
Color commentsDocument This
Automatically generate commentsTODO Highlight
The highlightedTODO
And can search allTODO
Without saying how to use it, here is a demo diagram to study it yourself:
Balance between writing and not writing comments
My personal view is this:
Functions that do not affect readability, are low in complexity, and do not interfere excessively with the outside world may not be commented.
Comments on expression statements
In a function, the expression statement comment can be simpler, I usually like the following figure, followed by a brief explanation.
function add(a, b) {
// sum ....
let sum = a + b
}
Copy the code
TODO comment
function say() {
// TODO:Write say specific content
console.log('say')}Copy the code
FIXME annotation
function fix() {
// FIXME:Delete the console.log method
console.log('fix')}Copy the code
Function annotations
In general, I divide it into ordinary functions and constructors.
Normal function comments:
/**
* add
* @param {Number} a- digital *@param {Number} b- digital *@returns {Number} result- Sum of two integers */
function add(a, b) {
// FIXME:Here we need to type the parameters a and b
let result = a + b
return (result)
}
Copy the code
Constructor comment:
class Kun {
/ * * *@constructor
* @param {Object} opt- Configure the object */
constructor(opt = {}) {
// Statement comment
this.config = opt
}
}
Copy the code
conclusion
As you can see from the code in open source projects, there are many different styles of annotation, and sometimes my own style of annotation varies from project to project, but I try to balance annotation and non-annotation as much as possible. The basic principles of annotation above should be followed.
But there’s no silver bullet in comments.
Function robustness (defensive programming)
You’ve all heard of defensive programming, right? Let it Crash. Let’s take a look at the following:
Look at the last sentence, the test tested so many scenarios, and finally the bar blew up. .
So, we can see that the core of defensive programming is:
Take all possible exceptions into account and handle them accordingly.
But personally, I think defensiveness depends on how important it is. In general, it’s impossible to handle every case, but an effective way to improve code robustness is to program defensively.
Thinking about a project
I took over a requirement, rewriting (completely reconstructing) the function of login and registration binding of Suning wechat mini program, and synchronizing the code to other Small programs of Suning (making code handover with the development of other small programs and assisting coder to complete the smooth version transition).
The importance of this project is self-evident. Due to the large user base and high risk, many scenarios need to be considered, such as:
-
Is online version rollback supported? That is, the front-end AB version solution is required (if there is any problem online, you can quickly switch to the old login solution).
-
Various types of verification are required: graphic verification code, SMS verification code, IP, man-machine, device fingerprint, risk control, exception handling, and abnormal buried point reporting.
-
Code-level considerations: Reduce overall response time and improve user experience through code optimization.
-
This section describes how to ensure that a single node failure does not affect the entire login process.
You will find that there are a lot of points to consider, and how to properly fulfill this requirement is quite difficult.
PS: There is an answer at the end of the article about how to ensure that a single node failure does not affect the whole login process.
So let me give you my personal thoughts about function robustness.
Several ways of front-end function robustness
The input parameter should be robust
After the advent of ES6, the input parameter writing of functions has been improved and optimized. Look at the following code
function print(obj = {}) {
console.log('name', obj.name)
console.log('age', obj.age)
}
Copy the code
Print function, the input parameter is obj by obj = {} to set the default parameter value, thereby increasing the robustness of the input parameter.
However, if the default value of the input parameter is {}, the obj.name in the function will be undefined. This is not robust enough, so let’s talk about the robustness of the expression statement in the function.
Function expression statements should be robust
Continuing with the previous example:
function print(obj = {}) {
console.log('name:', obj.name || 'Unknown Name')
console.log('age:', obj.age || 'Age unknown')}Copy the code
If you do that, you’ll find that the expression statement is a little more robust, but not good enough. It’s not abstract enough. Let’s decouple the expression statement a little bit differently, like this:
function print(obj = {}) {
const { name = 'Unknown Name', age = 'Age unknown' } = obj
console.log('name:', name)
console.log('age:', age)
}
Copy the code
Console. log(‘name:’, name) (‘name:’, ‘name ‘) (‘name:’, ‘name ‘)
Two layers of function exception handling
Personally, I think the above points can be classified into a plan level, that is:
Nip it in the bud and don’t let it happen in the first place.
But don’t forget, there is always a plan level to consider, and that is:
The exception still appears, how to deal with the exception.
The following two levels have been determined, so how to better handle all kinds of exceptions and improve the robustness of the function? I have the following views.
It is derivedtry/catch
The principle of
A lot of people don’t know how to use try/catch. First, js runs in the runtime environment provided by node.js, while node.js is written in C++. C++ has its own try/catch mechanism for exception handling. That means the underlying implementation of js try/catch is to call C++ try/catch directly over the bridge.
C++ try/catch has several features, such as:
Try /catch can only catch exceptions for the current thread.
This explains why a JS try/catch can only catch synchronous exceptions, but not asynchronous exceptions (which are executed in another thread).
This is my derivation, not the exact answer.
Here’s a blog I recommend:
C++ try, catch exception handling mechanism
If you’re interested, take a look.
Handle exceptions properly
Here are a few ways:
The first method: If the operation is synchronous, throw can be used to pass the exception
Look at the following code:
try {
throw new Error('hello godkun, i am an Error ')
console.log('The code after throw does not execute.')}catch (e) {
console.log(e.message)
}
Copy the code
The first thing we need to know is that a throw passes exceptions synchronously, meaning that the throw has the same context as the function that uses the throw to pass an error.
If an exception is thrown in a context where no try/catch is used, the program will crash.
If nodejs is used, a process-level uncaughtException should be added to catch the uncaughtException. Exception handling of unhandledRejection is usually added.
Second method: if the operation is asynchronous
There are three ways:
-
Use callback, such as NodeJS ‘Error First style
-
For complex cases, this can be done using an event-based approach, where the caller listens for the object’s error events
-
Use promises and async/await to catch exceptions
The question now is, how do you choose which? Here are some principles:
-
Simple scenario, using promises and async/await directly to catch exceptions
-
Complex scenarios, such as multiple errors, are best used with events
Third method: if there are both asynchronous and synchronous operations
What to do? In my opinion, the best way to do this is to use the latest syntax: async/await combined with promise and try/catch to catch exceptions for both synchronous and asynchronous operations.
Fourth method: Handle some abstraction and encapsulation of exceptions
Another way to improve the quality of functions is to abstract and encapsulate functions that handle exceptions. How do you abstract and encapsulate handling exceptions? There are several ways to do it:
-
The first approach: For NodeJS, exception handling is usually packaged as middleware, such as Express/KOA based exception middleware. Usually, the exception handling middleware is loaded as the last middleware in order to catch any errors that might have occurred with the previous middleware
-
Second approach: For the front end or NodeJS, it is possible to encapsulate exception handling into modules, similar to events.
-
Third way: Use the decorator pattern to decorate the exception handling module for a function, such as wrapping a try/catch layer around the current function by decorator.
-
Fourth approach: Wrapping exception handling in a uniform manner using functors (Monad) and the like in functional programming, where Monad and try/catch behave as containers, is a fairly powerful approach. There are a lot of exception handling techniques that can be extended from Monad, but I recommend using them with caution, because not everyone can understand them and consider the overall technical capabilities of the team. Of course, if you are a person, you can enjoy yourself.
To handle exceptions properly, you need to be able to determine which method to use to handle exceptions. I have outlined the specific options, and here I recommend a blog post:
The evolution of Callback Promise Generator async-await and exception handling
So far I have seen the most comprehensive exception handling blog, but WHAT I have said here are the main points that I think are more important, there are clear differences between the two, let’s merge and absorb.
How can I ensure that a single node failure does not affect the entire login process
For example, the login process requires 4 security authentications. In the usual way, if one of them fails, then all of them fail. But this is not robust enough. Let me just mention that a lot of people probably won’t notice.
The main solution is to change the chain of promise, which used to look like this:
The pseudocode is as follows:
auth().then(getIP).then(getToken).then(autoLogin).then(xxx).catch(function(){})
Copy the code
After robust adjustment, it can be written as follows:
The pseudocode is as follows:
auth().catch(goAuthErrorHandle).then(getIP).catch(goIPErrorHandle).then(function(r){})
Copy the code
The code has been tweaked to make the login process much more robust, and even if an error occurs, it can be handled and passed on to the next method.
My personal view on exception handling
Personally, I think the processing of exceptions should be analyzed according to the actual situation. I have the following views:
Consider the maintainability of the project and the technical level of the team
I used a more abstract method of handling exceptions, such as functors, in one requirement, but as a result, I had to make the subsequent requirement changes myself. Well, that’s exciting, because colleagues are not familiar with functional programming.
Anticipate the complexity and importance of the project in advance.
Such as when doing one of the more important business, no thought of exception handling need so detail at the beginning, and generally the first edition, demand does not involve a lot of exception handling, but subsequent demand of optimization, find exception handling is so many, led directly to the need to rewrite the exception handling code.
Therefore, when evaluating projects in the future, we should learn to try to reserve good pits in advance according to the importance of the project.
This is a programming model for the future.
conclusion
I’ve covered a lot of things about function robustness (defensive programming), basically the way the front-end or NodeJS handles exceptions in general. Handling exceptions is not a simple task, and the work must be combined with the business to determine the appropriate way to handle exceptions. In a word, more practice makes real knowledge.
note
- This article is easier to read, the difficulty is not big, and there will be some harvest.
- The explanation may not be complete, if there is any missing, welcome to share in the comments section, together with progress.
- For robustness, I don’t have a bill of lading meta-test, because I’m running out of words, and it’s going to be a long article, so I only recommend unit tests
Jest
, according to the official website documentation, in line with the idea of functional programming, no problem.
Previous excellent articles
Suffice it to say, after reading this article, your Git and Gerrit are fine.
Senior Git and Gerrit skills in junior high School
A story about how to read the source code of the NPM package.
Afraid to read the NPM package source code? Explain the philosophy behind Taro Init
eggs
I recently wrote an article that I found not much to read, but I think I wrote it well. This article will definitely inspire and help most front-end engineers.
Publicity wave, want to inspire more front-end partners still on the road:
Recommended books and must-have knowledge for front-end engineers in the new era
communication
In addition, I have already written two articles in this series, and I plan to finish three, but judging from the situation, the following are all hard products, and it is difficult to finish within 7,000 words. I will finish the following several articles, and I will try again.
You can follow my Nuggets blog or Github for updates on the series.
Nuggets series of technical articles on Github summarized as follows, if you feel good, click a star to encourage me, I will achieve the mission, continue to output high-quality articles.
github.com/godkun/blog
I am the source terminator, welcome technical exchange.
Also can go toFront end rhapsody groupBrainstorm together. Have want to add, because the person is full, can add my good friend first, I will invite you into the group.
The language of the wind
Finally: respect the original, reprint please indicate the source ha 😋