What is a good function?

It’s about evaluating a function in terms of its results. What are the consequences of writing a function?

  • The executable

    That’s fundamental. If the function doesn’t run, it doesn’t make sense.

    To ensure the execution of a function, two aspects should be considered: the logic of the function itself and the execution environment of the function. The function itself is logically executable. Needless to say, the execution environment of a function is prone to errors and omissions. If a function calls an external variable or function, it is necessary to consider whether the external variable exists and meets the requirements, and whether the external function can work properly. The ability to handle these situations is called robustness.

    On the other hand, if you write a function that is never called in your program, it is redundant code that should be removed and reduced. If this function is called more than once, it is valuable code. If it is called multiple times, it becomes reusable and valuable.

  • Complete the function

    This is the second fundamental. If a function does not do what it is supposed to do, its meaning is questionable.

    Further, if a function doesn’t do what it’s supposed to do, but does something else unexpected, it’s like a rat’s poop, messing with the program’s execution logic.

    To refine it: “Expected functionality” means that functions have names, and that should be reflected in the function name, which is semantics. Functions should not do “other unexpected things,” which are side effects and should be avoided.

  • Can be read

    A noun used to measure readability, usually called readability. Readability is the foundation of the development of modern programming languages. From binary, to assembly and other low-level languages, to today’s advanced languages, readability has been climbing all the way. High-level languages are supposedly much more readable than low-level languages, so why bother programming for readability?

    Consider the opposite example: How does the Web front end protect code assets?

    With the “big three” of current client-side browsers, HTML, CSS, and JavaScript, it is impossible to protect code assets. All solutions boil down to “reduced readability,” which makes it hard to read, and to some extent protects code assets from being understood and then modified and maintained.

    To improve readability, on the other hand, is to make it easier for yourself or others to understand, modify, and maintain.

So, a good function, it should be

  • As little executable, robust, redundant code as possible, and as much reusability as possible.
  • Complete function. The function name is semantic and indicates what the function does without side effects.
  • Readable, easy to understand, modify, and maintain again.

How to write a good function

This paper takes JavaScript as an example to illustrate the robustness, reusability, semantics, side effects and readability.

Robustness,

Examples of bad

function numberPlusOne(val){
  return val + 1
}Copy the code

The expectation is the result of adding one to the input number. But what if instead of a number, you enter a string of numbers, or something else that’s not a number?

Good example

function numberPlusOne(val){ if(typeof val === 'string') { val = parseFloat(val) } if(typeof val === 'number'){ if(! isNaN(val)) return val + 1 } return NaN }Copy the code

If you need to add large numbers, you need to consider JavaScript precision further.

reusability

Examples of bad

function formatProductPrice(productInfo){ if(! productInfo) return productInfo if(productInfo.price){ if(typeof productInfo.price === 'string') { productInfo.price = parseFloat(productInfo.price) } productInfo.price = isNaN(productInfo.price) ? '0.00' : ProductInfo. Price. ToFixed (2)} / / copy and paste to get to the next section, And replace the price for the originalPrice if (productInfo originalPrice) {if (typeof productInfo. OriginalPrice = = = 'string') { productInfo.originalPrice = parseFloat(productInfo.originalPrice) } productInfo.originalPrice = isNaN(productInfo.originalPrice) ? '0.00' : productInfo. OriginalPrice. ToFixed (2)} return productInfo}Copy the code

The expectation is that the two price fields price and originalPrice of the product are formatted in the same way.

Good example

function formatProductPrice(productInfo){ if(! productInfo) return productInfo formatPrice(productInfo, 'price') formatPrice(productInfo, 'originalPrice') return productInfo } function formatPrice(obj, key){ if(! obj[key]) return let val = obj[key] if(typeof val === 'string') val = parseFloat(val) obj[key] = val.toFixed && Val. ToFixed (2) | | '0.00'}Copy the code

Reusability is all about avoiding duplicate code. But in programming, it should be an optimization to consider, not a must. Reuse is considered in advance, but it is not reused for various reasons. In fact, reusability is not improved, but development efficiency may be reduced.

The semantic

Examples of bad

function add(a, b){
  return a + b
}Copy the code

The expectation is the result of calculating the sum of two numbers (add).

Good example

function sum(a, b){
  return a + b
}Copy the code

So how should Add satisfy its semantics?

Number.prototype.add = function(val){
  return this + val
}

let a = 1, b = 2
a.add(b)	//3Copy the code

A) add B) sum C) add D) add The semantics required for programming are based on the ability to understand the meaning of the language correctly. So programmers need to learn English well. The above example illustrates the improper semantics of function names. Common problems in programming are naming constants, variables and fields, and sometimes entangling multiple similar values, how to distinguish naming.

Side effects

// Const obj1 = {a: 1} const obj2 = {b: 2 } function extendWithSideEffect(obj1, obj2){ Object.assign(obj1, obj2) return obj1 } function extend(obj1, obj2){ return Object.assign({}, obj1, obj2) }Copy the code

The expectation is “object merge”, and both functions implement object merge and return the merged object. ExtendWithSideEffect has the side effect of changing the input parameter obj1 object content, which is currently expected as a side effect and should be avoided.

readability

Examples of bad

Function oneDayOfWorker(){init()} function init(){leaveHome()} function init(){leaveHome()} doSomeThing(work) } function work(){ doSomeThing(goHome) } function goHome(){ doSomeThing(sleep) }Copy the code

Good example

function oneDayOfProgramer(){
  leaveHome(()=>{
    work(()=>{
      goHome(sleep)
    })
  })
}

function leaveHome(callback){
  doSomeThing(callback)
}
function work(callback){
  doSomeThing(callback)
}
function goHome(callback){
  doSomeThing(callback)
}Copy the code

Better example

async function oneDayOfProgramer(){
  await leaveHome()
  await work()
  await goHome()
  sleep()
}

function transformPromise(fn){
  return new Promise(resolve=>{
    fn(resolve)
  })
}
function leaveHome(callback){
  return transformPromise(doSomeThing)
}
function work(callback){
  return transformPromise(doSomeThing)
}
function goHome(callback){
  return transformPromise(doSomeThing)
}Copy the code

The main readability problem this example illustrates is that instead of writing functions in chain form, you should organize them in a total-sub structure.

Let the main function be main, where A, B, C, and D are the subfunction definitions that need to be called in order, and A, B, C, and D are the subfunction calls.

“Chain” writing functions:

The main [a], a [b] [c] – > b > c [d] > d

Describe the main function to call only the beginning of the sub-function, in the sub-function definition to call other sub-functions, forming a “linked list” structure. The code reader needs to look at each subfunction to understand the functional logic of main.

Function of “total-minute” structure organization:

A → B → C → D

Description describes the call order of subfunctions in the main function, and the subfunctions define their respective implementation functions. The code reader can understand the functional logic from the main function, combined with the semantics of the sub-function names.

The above problem is a typical one that affects readability. There is more than one problem with readability, and some issues may be controversial and require consensus, hence the term “code style”. Different styles have differences and similarities. Learn more and compare them to sort out your best practices.

conclusion

“How to write a good function” is a subjective topic. Programmers have accumulated a large number of objective evaluation indicators in programming practice, some of which may be mutually restricted, such as reusability, extensibility and readability, which are not easy to improve together. As a result, there is little discussion of “best practices”.

However, the importance of writing functions well is self-evident. “Programming for a while, refactoring the crematorium.” Bad functions either affect the programmer’s mood at work or advance the next refactoring, neither of which is a good thing. How to solve the problem? Only line breaks. Well, line breaks are a code style specification that improves readability.

Looking at yourself, how do you evaluate your code? My advice is to read the source code of some of the most popular frameworks and libraries in today’s programming languages and think about how you could do it better yourself. This article is reading the source code in the process of feeling.