Getting web front-end to combat: JavaScrip functional programming principles Introduction

After a long time to learn and use object-oriented programming, let's step back and consider the complexity of the system.

After doing some research, I discovered the concept of functional programming, such as invariance and pure function. These concepts enable you to build function without side effects, and therefore easier to maintain system has other advantages.

In this article, we will pass a lot of code examples to explain in detail some of the relevant functional programming and important concepts.

What is functional programming

Functional Programming is a programming paradigm, a computer program to build the structure and style elements, it is considered to calculate the evaluation of mathematical functions and avoids state and a change in the variable data.

Pure function

When we want to understand functional programming, first you need to know basic concept is a pure function, but pure function is what the hell?

We know how pure function is a function of whether there is a very strict definition here?:

  • Given the same parameters, the same result is returned (also referred to as deterministic ).
  • It does not cause any side effects.

Given the same parameters, the same result is obtained

If given the same parameters, it returns the same result. Imagine that we want to implement a function to calculate the area of ​​a circle.

Function do not pure, receiving radius as a parameter, then calculates radius radius the PI:

let PI = 3.14;

const calculateArea = (radius) => radius * radius * PI;

calculateArea(10); // returns 314.0
web前端开发学习Q-q-u-n: 784783012 ,分享开发工具,零基础,进阶视频教程,希望新手少走弯路

Why is this an impure function? The reason is simple, because it uses a global object is not passed to the function as a parameter.

Now, imagine a number of mathematicians believe that the value of pi is actually 42 and changes the value of the global object.

Impure function get 10 10 42 = 4200. For the same parameters (radius = 10), we get a different result.

Fix it:

let PI = 3.14;

const calculateArea = (radius, pi) => radius * radius * pi;

calculateArea(10, PI); // returns 314.0

Now the value of PI is passed to the function as an argument, so that no external object is introduced.

  • Radius = 10 and the parameter for PI = 3.14, are always the same result: 314.0.
  • Radius = 10 and for PI = 42, always get the same result: 4200

Read the file

The following function reads an external file, it is not a pure function, the contents of the file at any time may be different.

const charactersCounter = (text) => `Character count: ${text.length}`;

function analyzeFile(filename) {
  let fileContent = open(filename);
  return charactersCounter(fileContent);
}

Random Number Generator

Any function depends on the random number generator function can not be pure.

function yearEndEvaluation() {
  if (Math.random() > 0.5) {
    return "You get a raise!";
  } else {
    return "Better luck next year!";
  }
}

No significant side effects

Pure function does not cause any side effects to be observed. Examples of side effects include visible objects or modify global parameter passed by reference.

Now, we want to realize a function that receives an integer and returns the integer are incremented and returns.

let counter = 1;

function increaseCounter(value) {
  counter = value + 1;
}

increaseCounter(counter);
console.log(counter); // 2

The impure function receives the value and reassign counter, to increase its value 1.

Functional Programming discourages variability. We modify the global object, but how to do to make it pure function? Simply return the added value of 1.

let counter = 1;

const increaseCounter = (value) => value + 1;

increaseCounter(counter); // 2
console.log(counter); // 1

IncreaseCounter pure function returns 2, but the counter value is still the same. Function returns the incremented value, without changing the value of the variable.

If we follow these two simple rules, it will be easier to understand our program. Now each function is isolated, it can not affect other parts of the system.

Pure function is stable, consistent and predictable. Given the same parameters, a pure function always returns the same result.

We need to consider the results of the same parameters have different circumstances, because it will never happen.

The benefits of pure function

Pure function is certainly easier to test the code, do not need to mock anything, so we can use pure function unit testing different context:

  • Given a parameter A, the function returns the desired value B
  • Given a parameter C, the function returns the desired value D

A simple example is a set of digital reception, incremented by 1 and the operation number of each such sand sculpture.

let list = [1, 2, 3, 4, 5];

const incrementNumbers = (list) => list.map(number => number + 1);

Receiving an array of numbers, each number is incremented using a map, and returns a new incremental list of numbers.

incrementNumbers(list); // [2, 3, 4, 5, 6]

For input [1,2,3,4,5], the expected output is [2,3,4,5,6].

If everyone on the program, interested in the web front end, want to learn to learn, plan to in-depth understanding of the industry friends, we can add our front-end learning buckle qun: 784,783,012 friends, whether you are a student or want to switch, I have welcomed, not Share regularly dry, finishing a web front end 2019 the latest learning materials and 0 Basics tutorial for everyone to share: learning the front we are serious

Immutability

Despite the time change or the same, pure function heavyweights are the same.

When data is immutable, its state can not be changed after it is created.

You can not change an immutable object, if you have to come hard, just need to deep copy a copy, then the copy operation.

In JS, we usually use a for loop, for each iteration i is the variable variable.

var values = [1, 2, 3, 4, 5];
var sumOfValues = 0;

for (var i = 0; i < values.length; i++) {
  sumOfValues += values[i];
}

sumOfValues // 15

For each iteration, i and sumOfValue change in state, but how do we deal with the variability in the traversal? The answer is to use recursion .

let list = [1, 2, 3, 4, 5];
let accumulator = 0;

function sum(list, accumulator) {
  if (list.length == 0) {
    return accumulator;
  }

  return sum(list.slice(1), accumulator + list[0]);
}

sum(list, accumulator); // 15
list; // [1, 2, 3, 4, 5]
accumulator; // 0

The above code is a sum function, it receives a vector value. Function calls itself recursively until the exit list is empty. For each "walk", we will add to the total value of the accumulator.

The use of recursion, remain variables constant. It does not change the list and accumulator variable. It maintains the same value.

Observation: We can reduce use to achieve this functionality. This discussion took content in higher-order functions in.

Construction of the final state of the object is also very common. Suppose we have a string, trying to convert the string to a url slug.

Ruby in object-oriented programming, we can create a class UrlSlugify, this class has a method to convert a string slugify input url slug.

class UrlSlugify
  attr_reader :text

  def initialize(text)
    @text = text
  end

  def slugify!
    text.downcase!
    text.strip!
    text.gsub!(' ', '-')
  end
end

UrlSlugify.new(' I will be a url slug   ').slugify! # "i-will-be-a-url-slug"

There imperative programming method used above, first of all lower-case letters what we want to do in each slugify process, and then delete the unused spaces, replace the last remaining spaces with a hyphen.

In this way it changes the input state in the whole process is clearly inconsistent with the concept of pure function.

Here can be optimized by a combination of the function or the function chain. In other words, the result of the function as an input of the next function, without modifying the original input string.

const string = " I will be a url slug   ";

const slugify = string =>
  string
    .toLowerCase()
    .trim()
    .split(" ")
    .join("-");

slugify(string); // i-will-be-a-url-slug
web前端开发学习Q-q-u-n: 784783012 ,分享开发工具,零基础,进阶视频教程,希望新手少走弯路

The code is mainly to do these things:

  • toLowerCase: Converts a string to all lowercase letters.
  • trim: Delete blank ends of the string.
  • split and join: replace all instances matching the given string replacement

Referential transparency

Then implement a square function:

const square = (n) => n * n;

Set to the same input, this function will always have the same net output.

square(2); // 4
square(2); // 4
square(2); // 4
// ...

2 will be passed as a parameter square function always returns 4. In this way we can square (2) into 4, our function is referentially transparent.

Basically, if a function always produces the same result for the same input, then it can be regarded as transparent.

With this concept, we can do a cool thing to remember is this function. Imagine a function

const sum = (a, b) => a + b;

These parameters are used to call it

sum(3, sum(5, 8));

sum (5, 8) always equals 13, you can do show operations:

sum(3, 13);

This expression 16 is always obtained, we can replace the entire expression with a constant value, and write it down.

Function is a citizen of JS

JS as a function of a citizen is the trend, the function can also be considered and used as a value to use the data.

  • It quoted from the constant and variable.
  • Which was passed as a parameter to other functions.
  • As a result of other functions return it.

The idea is to function as the value and function as a data transfer. In this way, we can combine different functions to create a new function with the new behavior.

If we have a function, which two values ​​are summed, then the value is doubled, as shown below:

const doubleSum = (a, b) => (a + b) * 2;

Differencing the corresponding two values, then the value is doubled:

const doubleSubtraction = (a, b) => (a - b) * 2;

These functions have similar logic, but with the difference that the operator functions. If we can be regarded as the function values ​​and passes them as parameters, we can construct a receiver operator functions and functions that use it within the function.

const sum = (a, b) => a + b;
const subtraction = (a, b) => a - b;

const doubleOperator = (f, a, b) => f(a, b) * 2;

doubleOperator(sum, 3, 1); // 8
doubleOperator(subtraction, 3, 1); // 4

f and use it to process the parameters a and b, where the sum transfer function and the subtraction function using doubleOperator behavior and create a new combination.

Higher-order functions

When we talk about higher-order functions, typically include the following:

  • One or more parameters as a function of
  • Return a result as a function of

doubleOperator function is to achieve a higher order function of the above, an operator because it function as a parameter and use it.

We often use the filter, map and reduce are higher-order functions, Look see see.

Filter

For a given set, we want to filter by attributes. filter function expects a true or false value to determine whether the element should be included in the result set.

If the callback expression is true, the filter function contains the elements in the result set, otherwise, it will not.

A simple example is when we have a set of integers, we only want to even.

Imperative

Use imperative way to get an array of all the even usually do:

  • Create an empty array evenNumbers
  • Through the array numbers
  • To push the even array evenNumbers
var numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var evenNumbers = [];

for (var i = 0; i < numbers.length; i++) {
  if (numbers[i] % 2 == 0) {
    evenNumbers.push(numbers[i]);
  }
}

console.log(evenNumbers); // (6) [0, 2, 4, 6, 8, 10]
web前端开发学习Q-q-u-n: 784783012 ,分享开发工具,零基础,进阶视频教程,希望新手少走弯路

We can also use the filter function to receive an even higher-order functions and returns a list of even number:

const even = n => n % 2 == 0;
const listOfNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
listOfNumbers.filter(even); // [0, 2, 4, 6, 8, 10]

Imperative approach is usually like this:

var filterArray = function(x, coll) {
  var resultArray = [];

  for (var i = 0; i < coll.length; i++) {
    if (coll[i] < x) {
      resultArray.push(coll[i]);
    }
  }

  return resultArray;
}
console.log(filterArray(3, [10, 9, 8, 2, 7, 5, 1, 3, 0])); // (3) [2, 1, 0]

Declarative way

For the above always, we want a more declarative approach to solve this problem as follows:

function smaller(number) {
  return number < this;
}

function filterArray(x, listOfNumbers) {
  return listOfNumbers.filter(smaller, x);
}

let numbers = [10, 9, 8, 2, 7, 5, 1, 3, 0];

filterArray(3, numbers); // [2, 1, 0]

Use smaller in function of this, starting to look a bit strange, but it is easy to understand.

The filter function of the second parameter above this, i.e. the value of x.

We can also use the map way to do it. Imagine a set of information

let people = [
  { name: "TK", age: 26 },
  { name: "Kaio", age: 10 },
  { name: "Kazumi", age: 30 }
]

We want to filter age greater than 21 years of age, with a filter mode

const olderThan21 = person => person.age > 21;
const overAge = people => people.filter(olderThan21);
overAge(people); // [{ name: 'TK', age: 26 }, { name: 'Kazumi', age: 30 }]

map

The main idea is to convert a collection of map function.

The method by applying a function to map all of its elements and building a new set of set based on the value returned by the conversion.

If we do not want to filter people older than 21, we want to do is show like this: TK is 26 years old.

Use imperative, we usually do:

var people = [
  { name: "TK", age: 26 },
  { name: "Kaio", age: 10 },
  { name: "Kazumi", age: 30 }
];

var peopleSentences = [];

for (var i = 0; i < people.length; i++) {
  var sentence = people[i].name + " is " + people[i].age + " years old";
  peopleSentences.push(sentence);
}

console.log(peopleSentences); // ['TK is 26 years old', 'Kaio is 10 years old', 'Kazumi is 30 years old']

Declarative do:

const makeSentence = (person) => `${person.name} is ${person.age} years old`;

const peopleSentences = (people) => people.map(makeSentence);

peopleSentences(people);
// ['TK is 26 years old', 'Kaio is 10 years old', 'Kazumi is 30 years old']

The whole idea is to convert a given array into a new array.

Another interesting question is HackerRank update the list of issues . We want to use the absolute value of an array to update its value.

For example, input [2,3, - 4,5] required output [1,2,3,4,5], - the absolute value of 4 is 4.

A simple solution is to place each updated set of values, a very dangerous practice

var values = [1, 2, 3, -4, 5];

for (var i = 0; i < values.length; i++) {
  values[i] = Math.abs(values[i]);
}

console.log(values); // [1, 2, 3, 4, 5]

We use Math.abs function converts the value of its absolute value and in-place update.

This is not the best way to do solution.

First of all, we learned the front invariance, know immutability make more consistent and predictable function, our idea was to create a new collection has all absolute.

Secondly, why not here use the map to "convert" all data

My first idea was to test Math.abs function handles only one value.

Math.abs(-1); // 1
Math.abs(1); // 1
Math.abs(-2); // 2
Math.abs(2); // 2

We want to convert each value to a positive value (absolute value).

Now know how to perform an absolute value operation on a value, this function may be used as a parameter passed to a function map.

Remember the higher-order functions can receive and use it as a parameter to a function? Yes, map function can do it

let values = [1, 2, 3, -4, 5];

const updateListMap = (values) => values.map(Math.abs);

updateListMap(values); // [1, 2, 3, 4, 5]

Reduce

Thought reduce function is a receiving function and a set of values ​​and returns a combination of these items created.

A common example is the total amount of acquired orders.

Suppose you are in a shopping site, we have 1, 2 product, product and product 3 4 Add to cart product (order) in. Now, we have to calculate the total number of the shopping cart:

To imperative way is a convenient list of orders and the amount of each product and the total amount of the sum.

var orders = [
  { productTitle: "Product 1", amount: 10 },
  { productTitle: "Product 2", amount: 30 },
  { productTitle: "Product 3", amount: 20 },
  { productTitle: "Product 4", amount: 60 }
];

var totalAmount = 0;

for (var i = 0; i < orders.length; i++) {
  totalAmount += orders[i].amount;
}

console.log(totalAmount); // 120

Use reduce, we can construct a function to calculate the sum and the processing amount to reduce transfer function as parameters.

let shoppingCart = [
  { productTitle: "Product 1", amount: 10 },
  { productTitle: "Product 2", amount: 30 },
  { productTitle: "Product 3", amount: 20 },
  { productTitle: "Product 4", amount: 60 }
];

const sumAmount = (currentTotalAmount, order) => currentTotalAmount + order.amount;

const getTotalAmount = (shoppingCart) => shoppingCart.reduce(sumAmount, 0);

getTotalAmount(shoppingCart); // 120

There shoppingCart, a function of receiving a current currentTotalAmount sumAmount, summing them, and the order of the objects.

You can also use the map to shoppingCart converted to a set amount, and then reduce function sumAmount functions.

const getAmount = (order) => order.amount;
const sumAmount = (acc, amount) => acc + amount;

function getTotalAmount(shoppingCart) {
  return shoppingCart
    .map(getAmount)
    .reduce(sumAmount, 0);
}

getTotalAmount(shoppingCart); // 120

getAmount product receiving object and returns only the amount value, i.e. [10,30,20,60], and then, by adding the reduce all the items together.

Examples of three functions

Read the works of each higher-order functions. Here is an example to show you, explain how to combine these three functions in a simple example.

When it comes to shopping carts, suppose we have this product in order list

let shoppingCart = [
  { productTitle: "Functional Programming", type: "books", amount: 10 },
  { productTitle: "Kindle", type: "eletronics", amount: 30 },
  { productTitle: "Shoes", type: "fashion", amount: 20 },
  { productTitle: "Clean Code", type: "books", amount: 60 }
]

If you want to shop with the car type the total number of books usually do:

  • Type of filter for the books
  • Use the map to convert the shopping cart is a collection amount.
  • All add up to reduce use.
let shoppingCart = [
  { productTitle: "Functional Programming", type: "books", amount: 10 },
  { productTitle: "Kindle", type: "eletronics", amount: 30 },
  { productTitle: "Shoes", type: "fashion", amount: 20 },
  { productTitle: "Clean Code", type: "books", amount: 60 }
]

const byBooks = (order) => order.type == "books";
const getAmount = (order) => order.amount;
const sumAmount = (acc, amount) => acc + amount;

function getTotalAmount(shoppingCart) {
  return shoppingCart
    .filter(byBooks)
    .map(getAmount)
    .reduce(sumAmount, 0);
}

getTotalAmount(shoppingCart); // 70
web前端开发学习Q-q-u-n: 784783012 ,分享开发工具,零基础,进阶视频教程,希望新手少走弯路

Guess you like

Origin blog.51cto.com/14568129/2441190