JavaScript中的函数式编程

What is Functional Programming? What is it that it offers?

什么是函数式编程? 它提供了什么?

Let’s talk about the raw way of writing functions. Functions perform certain set of actions according to our requirements which could include fetching data, updating State, changing a set of mutable values and updating the DOM and so on.

让我们谈谈编写函数的原始方法。 函数根据我们的要求执行某些操作,包括获取数据,更新状态,更改一组可变值以及更新DOM等。

And then our code base gets bigger and bigger and after a specific point, we have to deal with bugs. Arrrgh!

然后我们的代码库变得越来越大,在特定的点之后,我们必须处理错误。 啊!

Fixing bugs most of the time is a tedious process because you have to go through potential culprits and sometimes there’s a lot! We fix them, then we have more bugs and the vicious cycle continues! At some point we might just look at our code base and end up saying this works although I don’t really know how! It’s Magic! :D

大多数时候,修复错误是一个单调乏味的过程,因为您必须经历潜在的罪魁祸首,有时还很多! 我们修复了它们,然后有了更多错误,恶性循环还在继续! 在某些时候,我们可能只是看一下我们的代码库,最后说这行得通,尽管我真的不知道怎么做! 这是魔法! :D

As the magician you need to have a clear understanding of the tricks you pull. And if you are going to refactor your code or write a fresh one you might need to think about the Application State Updates in your code.

作为魔术师,您需要对自己的技巧有清晰的了解。 而且,如果您打算重构代码或编写新代码,则可能需要考虑代码中的应用程序状态更新。

Application State

申请状态

An Application State is an entirety of all the values that define an instance of your program. Some of the values will be derived from this State like the dynamic button color or the Dynamic content inside a ‘p’ tag. The user may also continuously interact with the State using Events.

应用程序状态是定义程序实例的所有值的整体。 一些值将从该状态派生,例如动态按钮颜色或'p'标签内的动态内容。 用户还可以使用“ 事件”与州持续互动。

The State holds the most important values which if not handled correctly causes bugs. So may be directly mutating the State values everywhere is not such a good idea then.

国家拥有最重要的价值观,如果没有正确处理,则会导致错误。 因此,在任何地方直接改变国家价值可能不是一个好主意。

How about keeping logic execution and State Updates separate?In other words, what if we divide our code base into Pure Functions and State Changes(a.k.a Side Effects)?

如何将逻辑执行和状态更新保持分开?换句话说,如果我们将代码库分为纯函数和状态更改(又名副作用)怎么办?

功能编程 (Functional Programming)

Functional Programming is a programming paradigm where you extensively use functions and more importantly most of them are Pure Functions.

函数式编程是一种编程范式,您可以在其中广泛使用函数,更重要的是,大多数函数都是纯函数。

But before diving into Pure Functions, two things that need to be addressed are Side Effects and High Order Functions.

但是在深入研究纯函数之前,需要解决的两件事是“副作用”和“高阶函数”。

副作用 (Side Effects)

Side Effects are very interesting here in Functional Programming context. Side Effects are anything that changes the State of the Program, like DOM updates, fetch requests, or even logging to console.

在函数式编程方面,副作用在这里非常有趣。 副作用是任何可以更改程序状态的东西,例如DOM更新,获取请求,甚至记录到控制台。

In other words any permanent change made by a function outside it’s local environment is a Side Effect.

换句话说,功能在其本地环境之外进行的任何永久更改都是副作用。

These Effects are what we actually need our program for. But we also need to ensure that they are managed properly.

这些效果是我们程序真正需要的。 但是我们还需要确保对它们进行适当的管理。

The goal here is to structure your code in a way that the Side Effects are not spread over everywhere but contained and limited to only where it is needed. You still need to write a lot of logic, and that’s where Pure Functions come into picture.

这里的目标是以一种方式将代码结构化,使副作用不会散布在任何地方,而只包含在需要的地方。 您仍然需要编写很多逻辑,这就是Pure Functions发挥作用的地方。

高阶函数 (High Order Functions)

Functions are First Class Citizens in JavaScript which means that:

函数是JavaScript中的一等公民,这意味着:

  • They can be assigned to any variable using anonymous functions.

    可以使用匿名函数将它们分配给任何变量。
  • They can be passed as arguments in another functions.

    它们可以作为参数传递给另一个函数。
  • They can also be returned from another functions like other data types.

    也可以从其他函数(如其他数据类型)返回它们。

This really helps in function compositions and the declarative style of coding which are prominent features of Functional Programming.

这确实有助于函数构成声明式编码风格,这是函数式编程的突出特征。

纯函数 (Pure Functions)

The whole idea of Pure Functions is to make your code easier to reason with. You know the input and you can easily predict the output.

纯函数的整个思想是使您的代码更易于推理。 您知道输入并且可以轻松预测输出。

Pure Functions are inspired from the mathematical definition of a function. In Math a function is represented by the equation:

纯函数受函数的数学定义启发。 在数学中,函数由以下方程式表示:

f(x)=y 

Here x is the input and y is the output and the function is only representing the relationship between the input and the output.

这里x是输入, y是输出,该函数仅表示输入和输出之间的关系。

A Pure Function just like a math function will give you always the same output given a particular input value with no Side Effects.

像数学函数一样,纯函数将在给定特定输入值的情况下始终为您提供相同的输出,而不会产生副作用。

A more rudimentary way of looking at Pure Functions is through Referential Transparency.

查看纯函数的一种更基本的方法是通过引用透明性。

A function is called as Referentially Transparent when the function call can be replaced with it’s evaluated value and it does not change the behavior of the program at all.

当函数调用可以用其求值代替并且完全不改变程序行为时,该函数称为“ 引用透明”

Let’s look at a Login Function and different ways of implementing it:

让我们看一下登录功能及其实现方式:

//Global Scope.
var globaldata={user:"xyz",loggedin:false,currentstate:"none"}
var userinput="Current State Info";


/* Impure way of handling login. Changing Global State,
operation is dependent upon global variable 'userinput',
no return value.
*/
var login=(globaldata)=>{
globaldata.loggedin=true;
globaldata.currentstate=userinput;
}


//Pure way of handling login.
var login=(globaldata,userinput)=>{
  var globaldatacopy={...globaldata};
  globaldatacopy.loggedin=true;
  globaldatacopy.currentstate=userinput;
  return globaldatacopy;
}

There are two changes being done in the pure form of ‘login function’. One is Immutability. We don’t want the input or any outside variables to be mutated which will be a Side Effect. The Side Effects will make our function harder to reason with when we write substantial amount of logic.

仅以“登录功能”的形式进行了两项更改。 一是不变性 。 我们不希望输入或任何外部变量发生突变,这将是副作用 。 当我们编写大量逻辑时, 副作用将使我们的功能更难以推理。

The other change is taking ‘userinput’ as a parameter as our function output relies on that variable. This makes the function output more predictable and reliable.

另一个变化是将“ userinput”作为参数,因为我们的函数输出依赖于该变量。 这使函数输出更加可预测和可靠。

Imagine a scenario where the ‘globaldata’ is not correct in which case you don’t have to look inside the pure ‘login function’ for any unwanted mutations, and if you know the inputs, you can easily predict it’s output. After all we all like Reliability!

想象一下“ globaldata”不正确的情况,在这种情况下,您不必在纯“ login函数”中查找任何不需要的突变,而且如果您知道输入,则可以轻松预测其输出。 毕竟我们都喜欢可靠性!

声明式和代码可重用性 (Declarative Style and Code Re-usability)

Another thing that Functional Programming really helps with is the Declarative way of coding.

函数式编程真正帮助的另一件事是声明式编码方式。

Declarative way and Imperative way are two styles of coding. The idea of Declarative style of coding is to generalize the recurrent parts of your code so that our main code looks smaller and neater. Take for example the method ‘map’ and ‘reduce’ in Array which is mostly used for looping through Array values. Frankly speaking I rarely use for and while loop nowadays with Arrays. I have been spoiled by ‘map’ and ‘reduce’. Sigh!

声明式和命令式是两种编码方式。 声明式编码的思想是概括代码的循环部分,以使我们的主代码看起来更小巧。 以Array中的“ map”和“ reduce”方法为例,该方法主要用于遍历Array值。 坦白说,我现在很少数组中使用forwhile循环。 我被' map'和' reduce'宠坏了。 叹!

But the fascinating part of it is that ‘map’ and ‘reduce’ both loop over the array using for/while loop behind the scenes. And when you do things behind the scenes, that’s the declarative style of coding because you’re not getting into the nitty-gritty details of how things should be done. Rather you’re sort of declaring what you want.

但是最有趣的部分是“ map”和“ reduce”都使用幕后的for / while循环在数组上循环。 而且,当您在幕后进行操作时,这就是声明性的编码风格,因为您无需深入了解应如何进行操作的细节。 而是您在声明自己想要什么。

You define ‘map’ and ‘reduce’ once and then you keep using it whenever you need. Take a look:

您只需定义一次“地图”“减少” ,然后在需要时继续使用它。 看一看:

//map function.
var map=(array,operator)=>{


let maparr=[];


for(let i=0;i<array.length;i++){
    maparr.push(operator(array[i],i,array))
}
return maparr;
}


map([1,2,3],(a)=>a*2); //(3) [2, 4, 6]


//reduce function.
var reduce=(...args)=>{
let [array,operator,initialval]=args;
let accumulator=args.length===3?initialval:array[0];


for(let i=3-args.length;i<array.length;i++){  
    accumulator=operator(accumulator,array[i],i,array);
}
return accumulator;
}


reduce([1,2,3],(acc,i)=>acc+i); //6

咖喱 (Currying)

I don’t know who named this concept but it sure does sound very Indian! Currying helps in making a function more flexible when it comes to invoking it and assigning parameters.

我不知道是谁命名这个概念的,但确实听起来很印度! 当调用和分配参数时,Currying有助于使功能更灵活。

The Special part about Currying is that at each call it takes only one input and then returns an output which will be a function if more parameters are needed or otherwise the output.

关于Currying的特殊部分是,在每次调用时,它只接受一个输入,然后返回一个输出,如果需要更多参数,则返回该函数,否则返回输出。

A simple example using currying:

使用currying的一个简单示例:

//Curried Function taking two parameters.
var repeatString=(string)=>(count)=>Array(count).fill(string).join("");


//Creating custom functions with currying.
var repeatStar=repeatString("*"); // (count)=>Array(count).fill("*").join("");
var repeatSpace=repeatString(" "); // (count)=>Array(count).fill(" ").join("");


for(let i=0;i<8;i++){
console.log(repeatSpace(8-i)+repeatStar(2*i+1));
}
/*
        *
       ***
      *****
     *******
    *********
   ***********
  *************
 ***************          */

The basic idea is to have only one input and one output for each function call which comes handy when we compose or pipe as we will see.

基本思想是每个函数调用只有一个输入和一个输出,这在我们进行组合或管道传输时会很方便。

功能组成 (Functional Composition)

Functional Composition is where you combine two functions to get a product function. The way to combine them is feed the output of one function as input to the next one. The equation looks like:

在功能组合中,您可以将两个功能组合在一起以获得乘积功能。 组合它们的方法是将一个功能的输出作为下一个功能的输入。 等式如下所示:

var resultant = (x)=>f(g(x)); //here the output of function 'g' is
provided as input to function 'f'.

Functional Composition can be used to combine multiple functions to create custom results. There are two ways of doing it:

功能组合可用于组合多个功能以创建自定义结果。 有两种方法:

Compose and Pipe

组成和管道

Imagine you have 3 functions f(), g() and h(). You want to pass the input through f, then g and then h. It should look something like:

假设您有3个函数f()g()h()。 您想要将输入传递给f,然后传递g,然后传递h。 它看起来应该像这样:

var output=(input)=>h(g(f(input))); 

This looks complex, somewhat like callback hell. To avoid such complexity we can use Compose and Pipe.

这看起来很复杂,有点像回调地狱。 为了避免这种复杂性,我们可以使用ComposePipe

What Compose does is take the functions(f,g,h) as arguments and then return a function that runs the input from the right to left.

Compose的工作是将函数( f,g,h )作为参数,然后返回一个从右到左运行输入的函数。

Pipe is similar to compose except that it runs the input from left to right. I don’t see the point of left-right but yeah that’s about the difference :D.

Pipe与compose相似,除了它从左到右运行输入。 我看不到左右的意思,是的,这是关于区别的:D。

// Compose Function
var compose=(...args)=>(input)=>args.reduceRight((acc,fn)=>fn(acc),input);
var resulant=compose(h,g,f); //(input)=>h(g(f(input)));


// Pipe Function
var pipe=(...args)=>(input)=>args.reduce((acc,fn)=>fn(acc),input);
var resulant=pipe(f,g,h); //(input)=>h(g(f(input)));

Important thing to notice here is that return of each function is passed as parameter to the next function which is why we need unary functions in composition although the first one can have multiple parameters.

这里要注意的重要一点是,每个函数的返回都作为参数传递给下一个函数,这就是为什么我们需要一元函数组成的原因,尽管第一个函数可以具有多个参数。

Now, how can we use Functional Composition and Currying together to produce something cool? I have made a mini example to show how we can use these concepts to extract parameters from a URL:

现在,我们如何才能一起使用“ 功能组合”和“ 咖喱”来制作一些很棒的东西? 我做了一个迷你示例,展示了如何使用这些概念从URL中提取参数:

var url="www.go.myurlexample.com/007w3?id1=123&id2=abc";


var getLastString=(param)=>(string)=>string.split(param).slice(-1).join();


var split=(param)=>(string)=>string.split(param);


var getParamObj=(arr)=>arr.reduce((acc,str)=>{
var arr=split("=")(str);
acc[arr[0]]=arr[1];
return acc;}
,{})


var getUrlParams=compose(getParamObj,split("&"),getLastString("?"));


getUrlParams(url); //{id1: "123", id2: "abc"}

Now all this could have been performed using a single function however that function would have been very customized for a single task.

现在,所有这些都可以使用单个功能执行,但是该功能将非常适合单个任务。

When we break big tasks into smaller tasks, what we can look for is re-usability of small functions like split that has been reused here. The same way we can reuse the other functions too in other parts of our code if we make them more generalized.

当我们将大型任务分解为较小的任务时,我们所寻找的就是小功能(如split)的可重用性,这些功能已在此处重用。 如果我们使它们更通用,则可以在代码的其他部分重用其他函数的相同方法。

国家管理 (State Management)

Most of the logic can be written using Pure Functions however the logic has to be applied to the State at some point and reducing those points of mutations greatly helps us in figuring out the cheeky little bugs in our code.

大多数逻辑都可以使用“纯函数”编写,但是必须在某个时候将逻辑应用于状态,减少这些突变点可以极大地帮助我们找出代码中一些小巧的小错误。

Funny thing is we can use functions over here also to update the State. Functions and Functions everywhere! :D

有趣的是我们可以在这里使用函数来更新状态。 功能无处不在! :D

The idea here is to make the State not directly accessible and to use functions to access or update it. Something like this:

这里的想法是使国家不能直接访问,并使用功能来访问或更新它。 像这样:

let createState=(initialvalue)=>{


    let state={...initialvalue};


    let getstate=()=>({...state});




    let setstate=(input)=>{


        if(input.constructor===Object)state={...state,...input};


        if(input.constructor===Function)state={...input({...state})};


    };


return [getstate,setstate];
}
var [getState,updateState]=createState({counter:0});


getState(); //{counter: 0}


updateState((oldState)=>{oldState.counter++;return oldState;});


getState(); //{counter: 1}

I am doing shallow copying in this example for simplicity but you get the gist of it. I hope this gives you a basic idea of Functional Programming in general.

为了简单起见,我在此示例中进行浅表复制,但是您可以理解它的要旨。 我希望这总体上为您提供了函数式编程的基本概念。

Have Fun with Functions!

玩转功能!

翻译自: https://medium.com/@ashwinkumar2438/functional-programming-in-javascript-9c608eab368b

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值