Surprise
In 1920, William Strunk Jr's English writing guide was published, which sets a standard for the English style and has been used so far. The code can actually be improved using a similar approach.
The following sections of this article are guidelines, not immutable laws. If you can clearly explain the meaning of the code, of course there are many reasons not to do so, but please remain vigilant and self-conscious. There is a reason they can pass the time test: because they are usually right. Deviation from the guide should have good reasons, and not simply because of whim or personal preference to do so.
Basically every part of the basic principles of writing can be applied to the code:
Make paragraphs the basic structure of an article: each paragraph corresponds to a theme.
Get rid of useless words.
- Use the active voice.
Avoid a series of loose sentences.
Put the relevant words together.
Declarative sentences are used in the active voice.
The parallel concept is in parallel with the structure.
These can all be used in the style of our code.
- Make the function a basic unit of code. Each function does one thing.
- Get rid of useless code
- Using the active voice
- Avoid a chain of loosely structured code
- Put the relevant code together.
- Expressions and statements are used in the active voice.
- The concept of parallelism is expressed in parallel code.
###
- Make the function a basic unit of code. Each function does one thing.
The essence of software development is writing. We have a software program that combines modules, functions, and data structures.
Understanding how to write functions and how to build them is a fundamental skill for software developers.
A module is a simple collection of one or more functions or data structures, and the data structure is how we represent the state of the program, but in the absence of a function, there is nothing interesting about the data structure itself.
JavaScript has three types of functions:
- AC function: function to perform I/O
- Functional functions: A collection of a series of instructions
- Mapping function: Give some input, return the corresponding output
All useful programs require I/O, and many programs follow some program order, but most functions should be like mapping functions: given some input, the function returns some corresponding output.
A function to do one thing: If your function is I/O sensitive, then do not mix the I/O with the mapping (calculation). If your function is for mapping, do not join I/O. Functional functions violate this rule. Functional functions also violate another rule: avoid writing loose sentences together.
The ideal function should be a simple, deterministic, purely functional function.
- Returns the same output given the same input
- No side effects
See "What is pure function"
2. Remove useless code
The words of vigorous are concise. A sentence should not contain useless words, a paragraph without useless sentences, just as painting should not have redundant lines, a machine without spare parts. This requires the author to try to use short sentences, avoid listing all the details, and list the topics in the outline, rather than saying everything. William STRUNK,JR, the English writing guide
Concise code is also important in software, because more code gives the bug a place to hide. Less code = Less space with bugs = fewer bugs.
The concise code is clearer because it has a higher signal-to-noise ratio: The reader can reduce the grammatical understanding of it more to understand its meaning. Less code = less grammatical noise = more information is passed.
Borrow a word from the English Writing Guide: Concise code is more powerful .
function secret (message) { return function () { return message; }};
The preceding section of code can be simplified to:
const secret = msg => () => msg;
This code is more readable for those familiar with the arrow functions (new features added by ES 2015). It removes the extra syntax: parentheses, function keywords, and return value statements.
The first version contains unnecessary syntax. For people familiar with arrow syntax, parentheses, function keywords, and return statements have no meaning. They exist just because there are many people who are unfamiliar with the new features of ES6.
ES6 is the language standard since 2015. You should be familiar with it.
Get rid of useless variables
Sometimes we tend to name some variables that don't actually need to be named. The reason is that the human brain can only store limited resources within the available capacity, and each variable must be used as a discrete quantum storage, occupying a small amount of memory space available to us.
For this reason, experienced developers tend to reduce the need for variable naming.
For example, in most cases, you should remove the variable and just create a variable that returns a value. The function name should be able to provide enough information to display its return value. Look at the following example:
const getFullName = ({firstName, lastName}) => { const fullName = firstName + ‘ ‘ + lastName; return fullName;};
And:
const getFullName = ({firstName, lastName}) => ( firstName + ‘ ‘ + lastName);
Another way that developers often use to reduce variables is to use the combination of functions and the style of Point-free.
Point-free style refers to the argument that defines a function without referencing its operation. The common style of point-free is mainly curry and function combination.
See an example of using curry:
const add2 = a => b => a + b;// Now we can define a point-free inc()// that adds 1 to any number.const inc = add2(1);inc(3); // 4
Now look at the Inc () function. Note that it does not have a function keyword, or = = syntax. There is no argument list because the parameter list is not used inside the function. Instead, it returns a function of how the parameter is handled.
Let's take a look at examples of using function combinations. A function combination is the process of applying a function result to another function. You may not realize that you are actually using the function combination all the time. When you call .map()
or promise.then()
function, you are using it. For example, most of the time its basic form, in fact, is like this: f (g (X)). In algebra, such a combination is written: f°g, called "G after F" or "F combination g".
When you combine two functions, you get rid of the variables that need to be stored in the intermediate return value. Let's take a look at the following code that can be simpler:
const g = n => n + 1;const f = n => n * 2;// With points:const incThenDoublePoints = n => { const incremented = g(n); return f(incremented);};incThenDoublePoints(20); // 42// compose2 - Take two functions and return their compositionconst compose2 = (f, g) => x => f(g(x));// Point-free:const incThenDoublePointFree = compose2(f, g);incThenDoublePointFree(20); // 42
Similar effects can be achieved with the use of a functor function. Similar effects can be achieved with the use of a functor function. The following code is an example of using an imitation function:
const compose2 = (f, g) => x => [x].map(g).map(f).pop();const incThenDoublePointFree = compose2(f, g);incThenDoublePointFree(20); // 42
In fact, when you use the Promise chain, you are basically using this method.
In fact, each codec library has at least two versions of the practical approach: compose ()
combining functions from right to left, functions pipe()
combining functions from left to right.
Lodash These two functions as the compose()
and flow()
. When I use them in lodash, I usually introduce them like this:
import pipe from ‘lodash/fp/flow‘;pipe(g, f)(20); // 42
However, the following code is less, and the same thing is done
const pipe = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x);pipe(g, f)(20); // 42
If the combination of functions is as unfathomable as an alien to you, and you are not sure how to use them, then take a careful look at the previous words:
The essence of software development is writing. We combine modules, functions, and data structures together to form a software program.
As a result, you can conclude that understanding the tool meaning and object combination of a function is like a family manual worker who understands how to use the same basic skills as a drill and a nail gun.
When you combine different functions with a set of instructions and intermediate variables, it's like using tape and crazy glue to get things together casually.
Please remember:
- If you can express the same meaning in less code and not change or confuse the meaning of the code, you should do so.
- This should also be done if you can use fewer variables to achieve the same purpose without altering or confusing the original intent.
3. Using the active voice
The active voice is more direct and powerful than the passive voice. --William STRUNK,JR, the English writing guide
The more direct the name, the better.
myFunction.wasCalled()
Superior myFunction.hasBeenCalled()
.
createUser()
Superior User.create()
.
notify()
Superior Notifier.doNotification()
.
Use the Yes or no problem form when naming assertions or Boolean variables:
isActive(user)
Better thangetActiveStatus(user)。
isFirstRun = false;
Superior firstRun = false;
.
The named function uses the verb form
increment()
Superior plusOne()
.
unzip()
Superior filesFromZip()
.
filter(fn, array)
Superior matchingItemsFromArray(fn,array)
.
Event handling
The function of event handlers and life cycles is an exception, avoid using verb forms, because they are usually meant to explain what to do at this time rather than what they are doing as subjects themselves. Functions should be consistent with naming.
element.onClick(handleClick)
Superior element.click(handleClick)
.
component.onDragStart(handleDragStart)
Better thancomponent.startDrag(handleDragStart)。
The second of the two naming methods in this example looks more like we're trying to trigger something than responding to the event.
Life cycle functions
Suppose you have a component that has a life cycle function that calls an event-handling function before it is updated, with the following naming methods:
componentWillBeUpdated(doSomething)
componentWillUpdate(doSomething)
componentWillUpdate(doSomething)
The first kind of naming uses the passive voice. This approach is a bit more intuitive than the other way around.
The second way is slightly better, but it means that this life cycle method calls a function. It's componentWillUpdate(handler)
like this component is going to update an event handler, which deviates from the original intent. Our intent was: "Call event handling before component Updates" is beforeComponentUpdate()
more appropriately named.
It can be more streamlined. Since these are all methods, the subject (i.e. the component itself) is actually determined. When this method is called, it repeats with the subject. Imagine seeing this piece of code as you would see component.componentWillUpdate()
. It's like saying, "Jimmy, Jimmy's going to have steak at noon." You don't actually need to hear a duplicate name.
component.beforeUpdate(doSomething)
Better thancomponent.beforeComponentUpdate(doSomething)
Functional Mixins is a method of adding properties and methods to object objects. Functions are added one after the other, just like a pipe flow, or as an assembly line. Each functional mixin function has one instance
as input, attaches something extra, and then passes it to the next function, just like the assembly line.
I tend to name the mixin function with adjectives. You can also use a suffix such as "ing" or "able" to denote the meaning of an adjective. For example:
const duck = composeMixins(flying,quacking);
const box = composeMixins(iterable,mappable);
Avoid a series of loose statements
A series of sentences will soon be boring and lengthy. William STRUNK,JR, the English writing guide
Developers often talk about a series of events connected to a whole process: a series of loose statements that are designed to exist one after the other. But over-using such a process can lead to code that is as intricate as pasta.
This sequence is often repeated, albeit slightly different, and sometimes unexpectedly deviated from the formal. For example, one user interface might share the same component code with another user interface. The cost is that the code may be divided into different lifecycles and a component may be managed by several different blocks of code.
Refer to the following example:
const drawUserProfile = ({ userId }) => { const userData = loadUserData(userId); const dataToDisplay = calculateDisplayData(userData); renderProfileData(dataToDisplay);};
This code does three things: load the data, calculate the relevant state, and then render the content.
In the modern front-end application framework, these three things are separated from each other. By separating, everything can be better combined or expanded.
For example, we can completely replace the renderer without affecting the rest; for example, react has a rich custom renderer: Reactnative,webvr aframe for native iOS and Android applications, reactdom for server-side rendering/ Server, and so on.
Another problem is that you cannot simply calculate the data to display and cannot generate a display page without loading the data for the first time. What if you already loaded the data? Then your computational logic becomes redundant in the next call.
Separation also allows individual components to be measured independently. I like to add a lot of unit tests to my application and display the test results so that I can see them whenever I have any changes. However, if I tried to test the ability to load data and render it, I wouldn't be able to test the rendering part with just some fake data. Saving ...
I can't get the results right away from the unit test. The separation of functions allows us to perform independent testing.
This example shows that the separation function allows us to participate in the different life cycles of the application. You can trigger the load function of the data after the application loads the component. Calculations and rendering can occur when the view changes.
The result is a clearer description of the software's responsibilities: the ability to reuse components of the same structure and the life cycle of callbacks, and better performance, and in the latter workflow, we also save unnecessary labor.
5. Put the relevant code together.
Many frameworks or boilerplate programs have a way of organizing a program, which is divided by file type. If you do a small calculator or to-do application, this is fine, but if it's a large project, a better approach is to group the files by function.
The following is an example of a to-do application, with two file organization structures.
Classification by file type
.├── components│ ├── todos│ └── user├── reducers│ ├── todos│ └── user└── tests ├── todos └── user
Classification by file function
.├── todos│ ├── component│ ├── reducer│ └── test└── user ├── component ├── reducer └── test
According to the function of organizing files, you can effectively avoid in the folder view constantly scrolling up and down, go directly to the function folder to find the files to edit.
Organize the files according to the function.
6. Declarative sentences and expressions use the active voice.
"Make a definite assertion. Avoid boredom, no-shine, hesitation, no tone. The use of "not" should be expressed as negative or opposite, rather than as a means to escape. William STRUNK,JR, the English writing guide.
isFlying
Superior isNotFlying
.
late
Superior notOnTime
.
If statement
if (err) return reject(err);// do something...
Better than this:
if (!err) { // ... do something} else { return reject(err);}
Three-dimensional expression
{ [Symbol.iterator]: iterator ? iterator : defaultIterator}
Better than the following form:
{ [Symbol.iterator]: (!iterator) ? defaultIterator : iterator}
Try to choose a negative sentence with strong tone
Sometimes we only relate to whether a variable is missing, so using the active syntax will let us be forced to add one !
. In these cases, it is better to use a strongly negative sentence. The word "not" !
is relatively weak in tone.
if (missingValue)
Superior if (!hasValue)
.
if (anonymous)
Superior if (!user)
.
if (isEmpty(thing))
Superior if (notDefined(thing))
.
Avoid using null and undefined parameter types when function calls
Do not use undefined
or null
parameters as optional arguments to the function. Use optional parameters as much as possible Object
. Try to use optional object as a parameter.
const createEvent = ({ title = ‘Untitled‘, description = ‘‘, timeStamp = Date.now()}) => // ...// later...const birthdayParty = createEvent({ title = ‘Birthday Party‘, timeStamp = birthDay});
Better than
const createEvent( title = ‘Untitled‘, description = ‘‘, timeStamp = Date.now());// later...const birthdayParty = createEvent( ‘Birthday Party‘, undefined, // This was avoidable birthDay);
Using parallel structures
Parallel structures need to be as similar as possible to the structure expression semantics. The shape of the format allows the reader to understand the meaning of the different statements is similar. -William STRUNK,JR, "English Writing Guide"
In practical applications, there are some additional problems that are not resolved. We may do the same thing over and over again. When such a situation arises, there is an abstraction of space. Find the same part and abstract it into a common part that can be used in different places at the same time. This is actually something that many frameworks or function libraries do.
In the case of UI controls, for example. More than 10 years ago, it was also common to use jquery to write code that mixes components, logic applications, and network I/O. Then people began to realize that we could also use the MVC framework in Web applications, so people gradually began to separate the model from the logic of UI updates.
The final structure is that the Web application uses the method of the component model, which allows us to build our UI components with JSX or HTML templates.
This allows us to control the updates of different components in the same way, without having to write duplicate code for each component's update.
People who are familiar with the component can easily see how each component works: There are some code that represents declarative markup for UI elements, and some are used for event handlers and callback functions that are used in the life cycle, which are executed when needed.
When we find a pattern for similar problems, anyone familiar with the pattern can quickly understand the code.
Conclusion: The code should be concise, but not simplistic.
The words of vigorous are concise. A sentence should not contain useless words, a paragraph without useless sentences, just as painting should not have redundant lines, a machine without spare parts. This requires the author to try to use short sentences, avoid listing all the details, and list the topics in the outline, rather than saying everything. -william strunk,jr., "English Writing Guide"
ES6 was standardized in 2015, but in 2017, many developers avoided the functionality of concise arrow functions, implicit returns, rest and propagation operations. People make excuses for writing more readable code, but only because people are more familiar with the old patterns. This is a huge mistake. Familiarity comes from practice, and the obvious reason to be familiar with the simplicity of ES6 in the ES5 is obvious: This code is more concise than the thick syntax function code.
The code should be concise, not simplistic.
The simple code is:
- Fewer bugs
- More convenient to debug
The bug is usually this:
- It takes time and power to repair.
- possible to introduce more bugs
- Disrupt the normal work flow
So the concise code should be:
- Easy to write
- Easy to read
- Easy Maintenance
It is worthwhile for developers to learn and use new technologies such as curry. This also allows readers to familiarize themselves with the new knowledge. If we still use the same approach, it is also disrespectful to the person reading the code, as if they were using the child's tone when talking to the baby.
We can assume that the reader does not understand the implementation of this code, but do not assume that the person reading the code is stupid, or if they do not understand the language.
The code should be concise, but not plunges. Plunges is a waste and an insult. To practise in practice, devote energy to familiarity, learning a new programming grammar, a more dynamic style.
The code should be concise rather than simplistic.
Copyright notice this translation is for study, research and communication purposes only, welcome non-commercial reprint. Reprint please indicate the source, translator and the full link to the translation. To get the markdown source text for this article that contains the above information, please click here.Web JavaScript
JavaScript Coding Guide