• Master the JavaScript Interview: What is a Pure Function?
  • By Eric Elliott
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…
  • Translator: niayyy -s
  • Proofread by: chaingangway, Amy shensh

Mastering JavaScript Interview: What are Pure functions?

Pure functions are essential for a variety of purposes, including functional programming, reliable concurrency, and React + Redux-constructed applications. But what does a pure function mean?

We answer this question with a free lesson from Learn JavaScript with Eric Elliott:

Before we tackle the question of what a pure function is, it might be better to take a closer look at what a function is. Perhaps there is a different way of looking at functions that makes functional programming easier to understand.

What is a function?

A function is a procedure: it takes some input called parameters and produces some output called return values. Functions can be used for the following purposes:

  • Mapping: Produces some output based on input values. Function maps input values to output values.
  • Procedural: A function can be called to perform a series of steps. This sequence of steps is called a procedure, and programming in this way is called process-oriented programming.
  • I/O: Some functions exist to communicate with other parts of the system, such as screens, storage, system logs, or the network.

mapping

Pure functions are all about mappings. Function maps input parameters to return values, which means that for each set of inputs, there is a corresponding output. The function takes input and returns the corresponding output.

Math.max() takes a list of numbers as arguments and returns the maximum number:

Math.max(2.8.5); / / 8
Copy the code

In this example, 2,8, and 5 are parameters. They are the values passed to the function.

Math.max() is a function that can take any number of arguments and return the maximum argument value. In this case, the maximum number we pass in is 8, which corresponds to the number returned.

Functions are very important in computation and mathematics. They help us process the data in the right way. Good programmers give functions descriptive names so that when we look at the code, we know what the function does.

Math also has functions that work very much like functions in JavaScript. You’ve probably seen algebraic functions. They look like this:

f(x) = 2x

That means we’re declaring a function called f, which takes an argument called x and multiplicates x by 2.

To use this function, we simply provide a value for x:

f(2)

In algebra, this means exactly the same as the following:

4

So you can replace 4 anywhere you see f(2).

Now let’s describe this function in JavaScript:

const double = x= > x * 2;
Copy the code

You can check the output of the function using console.log() :

console.log( double(5));/ / 10
Copy the code

Remember I said that in math functions, you can replace f of 2 with 4? In this case, the JavaScript engine replaces double(5) with 10.

So console.log(double(5)); With the console. The log (10); The same

This is true because double() is a pure function, but replacing double(5) with 10 changes the meaning of the function if double() has side effects, such as saving the value to disk or printing it to the console.

If you want to reference transparency, you need to use pure functions.

Pure functions

A pure function is a function where:

  • Given the same input, the same output will always be returned.
  • No side effects.

If you call a function properly without using its return value, it is definitely not a pure function. For pure functions, this is equivalent to not being called.

I suggest you prefer to use pure functions. This means that if it is feasible to use pure functions to fulfill program requirements, they should be preferred. Pure functions take some input and return some output based on the input. They are the simplest reusable blocks of code in your program. Perhaps the most important design principle in computer science is KISS (Keep it simple). I prefer to keep it simpleton simple. Pure functions are the best form of foolproof simplicity.

Pure functions have many beneficial features and form the basis of functional programming. Pure functions are completely independent of external state, and as such, they are immune to all the problems associated with shared mutable state. Their independent nature also makes them an excellent choice for parallel processing across multiple cpus as well as entire distributed computing clusters, making them critical for many types of scientific and resource-intensive computing tasks.

Pure functions are also very self-contained — they can be easily moved, refactored, and reorganized within the code, making the program more flexible and adaptable to future changes.

Share status issues

A few years ago, I was developing an application that allowed users to search an artist’s database and load a playlist of that artist’s music into a Web player. Around the time Google Instant went live, it would display real-time search results when a search query was entered. Ajax-driven auto-complete is all the rage.

The only problem is that user input is often faster than the API’s auto-complete search and return response, resulting in some strange errors. This triggers a race condition, in which newer results may be replaced by expired ones.

Why is that? Because each AJAX success handler has the right to directly update the list of suggestions displayed to the user. The slowest AJAX requests can always get the user’s attention through blind substitutions, even though the results of those substitutions may be newer.

To solve this problem, I created a suggestion manager — a single data source to manage the status of the query suggestions. It knows that there is currently a pending AJAX request, and that when the user enters new content, the pending AJAX request will be canceled before the new request is issued, so only one response handler at a time will be able to trigger a UI state update.

Any kind of asynchronous operation or concurrency can lead to similar race conditions. A race condition occurs if the output depends on the order of uncontrollable events (e.g., network, device delay, user input, randomness, etc.). In fact, if you are using a shared state and that state depends on a number of uncertainties, the output is, in short, unpredictable, which means it cannot be properly tested or fully understood. As Martin Odersky, creator of the Scala language, said:

Uncertainty = parallel processing + mutable state

Program determinism is usually an ideal property in computation. You might think this is ok because JS runs in a single thread and is therefore immune to parallel processing problems, but as the AJAX example shows, a single-threaded JS engine does not mean there is no concurrency. In contrast, there are many concurrent sources in JavaScript. API I/O, event listening, Web workers, iframe, and timeouts can all introduce uncertainty into a program. Combine this with shared state and you have a solution to the bug.

Pure functions can help you avoid these bugs.

Given the same input, always return the same output

Using the double() function above, you can replace the function call with the result, and the program still has the same meaning — double(5) always has the same meaning as 10 in the program, no matter the context, no matter how many times or when it is called.

But you can’t say that about all functions. Some functions rely on information other than the parameters you pass in to produce results.

Consider the following example:

Math.random(); / / = > 0.4011148700956255
Math.random(); / / = > 0.8533405303023756
Math.random(); / / = > 0.3550692005082965
Copy the code

Although we don’t pass any arguments to any of the function calls, they all produce different output, which means math.random () is not a pure function.

Math.random() generates a new random number between 0 and 1 every time it is run, so obviously you can’t just replace it with 0.4011148700956255 without changing the meaning of the program.

That will produce the same result every time. When we ask a computer to generate a random number, it usually means we want a different result from the last time. What’s the point of a pair of dice with the same number printed on each side?

Sometimes we have to ask the computer for the current time. We’re not going to get into the details of how a time function works. Just copy the following code:

const time = (a)= > new Date().toLocaleTimeString();

time(); // => "5:15:45 PM"
Copy the code

What happens if you call the time() function instead of the current time?

It always outputs the same time: the time at which the function call was replaced. In other words, it can only produce correct output once a day, and only if you run the program at the exact moment of the replacement function.

Obviously, time() is not like double().

A function is pure if it always produces the same output given the same input. You may remember this rule from algebra class: The same input value will always map to the same output value. However, many input values may map to the same output value. For example, the following functions are pure functions:

const highpass = (cutoff, value) = > value >= cutoff;
Copy the code

The same input value will always map to the same output value:

highpass(5.5); // => true
highpass(5.5); // => true
highpass(5.5); // => true
Copy the code

Many input values may map to the same output value:

highpass(5.123); // true
highpass(5.6);   // true
highpass(5.18);  // true

highpass(5.1);   // false
highpass(5.3);   // false
highpass(5.4);   // false
Copy the code

A pure function must not depend on any external mutable state, since it is no longer deterministic or referentially transparent.

Pure functions have no side effects

A pure function has no side effects, meaning it cannot change any external state.

invariance

JavaScript object parameters are referential, which means that if a function changes an attribute on an object or array parameter, it changes the state that is accessible externally to that function. A pure function may not change external states.

Consider this modified, impure addToCart() function:

// The impure addToCart function changes the existing CART object
const addToCart = (cart, item, quantity) = > {
  cart.items.push({
    item,
    quantity
  });
  return cart;
};


test('addToCart()', assert => {
  const msg = 'addToCart() should add a new item to the cart.';
  const originalCart =     {
    items: []};const cart = addToCart(
    originalCart,
    {
      name: "Digital SLR Camera".price: '1495'
    },
    1
  );

  const expected = 1; // Number of items in cart
  const actual = cart.items.length;

  assert.equal(actual, expected, msg);

  assert.deepEqual(originalCart, cart, 'mutates original cart.');
  assert.end();
});

Copy the code

This function is called by passing the Cart object, adding goods and quantity of goods to the Cart object. The function then returns the same Cart object and adds the item.

The problem with this is that we just changed some of the shared states. Other functions might depend on the cart object state — the state before the function was called — and now that we have changed this shared state, we have to worry about what will happen to the program logistically if we change the order of function calls. Refactoring code can lead to bugs that can break orders and lead to customer dissatisfaction.

Now consider this version:

// The pure addToCart() function returns a new cart object
// This does not change the original object
const addToCart = (cart, item, quantity) = > {
  const newCart = lodash.cloneDeep(cart);

  newCart.items.push({
    item,
    quantity
  });
  return newCart;

};


test('addToCart()', assert => {
  const msg = 'addToCart() should add a new item to the cart.';
  const originalCart = {
    items: []};// deep-freeze on NPM
  // If the original object is changed, an error is thrown
  deepFreeze(originalCart);

  const cart = addToCart(
    originalCart,
    {
      name: "Digital SLR Camera".price: '1495'
    },
    1
  );


  const expected = 1;  // Number of items in cart
  const actual = cart.items.length;

  assert.equal(actual, expected, msg);

  assert.notDeepEqual(originalCart, cart,
    'should not mutate original cart.');
  assert.end();
});

Copy the code

In this example, we have an array nested within the object, which is why I want to do the deep clone. This is more complicated than what you would normally have to deal with. For most things, you can break it down into smaller pieces.

For example, Redux lets you compose reducers instead of addressing the entire application state in each reducers. As a result, you don’t have to create a deep clone every time you update a small part of the entire application state. Instead, you can use non-destructive array methods, or object.assign (), to update a small part of the application state.

It’s your turn. Fork the codepen code and convert an impure function to a pure one. Make the unit tests pass without changing the tests.

Explore the series

  • What is a closure?
  • What is the difference between class and stereotype inheritance?
  • What is a pure function?
  • What does the function consist of?
  • What is functional programming?
  • What is Promise?
  • Soft skills

This post has been included in the Composing Software book. Buy this book | | index < prev | page >


Eric Elliott is an expert in distributed systems and the author of the books “Composing Software” and “Programming JavaScript Applications.” As co-founder of DevOffgod. IO, he teaches developers the skills they need to work remotely and achieve work/life balance. He established and consulted with the crypto development team and contributed to software experiences for Adobe, Zumba Fitness, The Wall Street Journal, ESPN, BBC, and top recording artists including Usher, Frank Ocean, and Metallica.

He enjoys a telecommuting lifestyle with the most beautiful women in the world.

If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.


The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.