进阶学习1:函数式编程FP——概念头等函数高阶函数常用高阶函数模拟

Posted JIZQAQ

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了进阶学习1:函数式编程FP——概念头等函数高阶函数常用高阶函数模拟相关的知识,希望对你有一定的参考价值。

目录

一、函数式编程概念

1.面向过程编程Procedure Oriented Programming(POP):

2.面向对象编程Object Oriented Programming(OOP):

3.函数式编程Functional Programming(FP):

二、函数是一等公民 First-class Function(头等函数)

1.函数作为变量

2.高阶函数 (Higher-order function)

函数作为参数

函数作为返回值

Once函数

使用高阶函数的意义

3.常用高阶函数模拟

forEach

map

filter

every

some

 


一、函数式编程概念

函数式编程Functional Programming(FP),是编程范式之一,其他编程范式还有面向过程编程、面向对象编程。

老师没有过多展开另外两个,于是课后我自己查了一下资料。

1.面向过程编程Procedure Oriented Programming(POP):

分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。常见的有C语言。

//1.面向过程编程
//按照步骤一步一步来的
let num1 = 2
let num2 = 3
let sum = num1 + num2
console.log(sum)

2.面向对象编程Object Oriented Programming(OOP):

把构成问题的事务分解成各个对象和类,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行,通过封装继承和多态来演示是事物事件的联系。常见的有C++、C#、Java

面向对象编程小例子解析

https://blog.csdn.net/zzh920625/article/details/41986599

3.函数式编程Functional Programming(FP):

虽然也可以归结到面向过程的程序设计,但其思想更接近数学计算。

我们首先要搞明白计算机(Computer)和计算(Compute)的概念。在计算机的层次上,CPU执行的是加减乘除的指令代码,以及各种条件判断和跳转指令,所以,汇编语言是最贴近计算机的语言。而计算则指数学意义上的计算,越是抽象的计算,离计算机硬件越远。对应到编程语言,就是越低级的语言,越贴近计算机,抽象程度低,执行效率高,比如C语言;越高级的语言,越贴近计算,抽象程度高,执行效率低,比如Lisp语言。

程序的本质:根据输入通过某种运算获得相应的输出,程序开发过程中涉及到很多输入和输出的函数。

函数式编程中的函数指的不是程序中的函数(方法),而是数学中的函数映射关系。

函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。

函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数。

// 3.函数式编程
// 有着可变的输入,一个映射关系,获得输出。并且相同输入只会得到一个输出。
function add (n1, n2) {
    return n1 + n2
}
let sum = add(2, 3)
console.log(sum)

二、函数是一等公民 First-class Function(头等函数)

当一门编程语言的函数可以被当作变量一样用时,则称这门语言拥有头等函数javascript就是拥有头等函数)。

  • 函数可以被赋值给一个变量
  • 函数可以被当作参数传递给其他函数
  • 函数可以作为另一个函数的返回值

1.函数作为变量

// 把函数赋值给变量
let fn = function () {
console.log('Hello First-class Function') }
fn()
// 一个示例
const BlogController = {
index (posts) { return Views.index(posts) },
show (post) { return Views.show(post) },
create (attrs) { return Db.create(attrs) },
update (post, attrs) { return Db.update(post, attrs) }, destroy (post) { return Db.destroy(post) }
}
// 优化
const BlogController = {
  index: Views.index,
  show: Views.show,
  create: Db.create,
  update: Db.update,
  destroy: Db.destroy
 22
}

2.高阶函数 (Higher-order function)

  • 可以把函数作为参数传递给另一个函数
  • 可以把函数作为另一个函数的返回结果

函数作为参数

好处:更加灵活,不用在意被调用的函数内部具体实现的细节。

//DEMO1
// forEach遍历数列中每一项
function forEach (array, fn) {
    for (let i = 0; i < array.length; i++) {
        fn(array[i])
    }
}
//上课测试样例,需要完成的是打印出数列里的每一项。
let arr = [1, 3, 4, 7, 8]
forEach(arr, function(item){
    console.log(item)
})


//DEMO2
// filter 返回满足fn的数字
function filter (array, fn) {
    let results = []
    for (let i = 0; i < array.length; i++) {
        if (fn(array[i]))             {
            results.push(array[i])
        }
    }
    return results
}
//上课测试样例,需要返回能被2整除的数字
let arr = [1, 3, 4, 7, 8]
let result = filter(arr, function (item){return item % 2 === 0})
console.log(result)

实际测试结果:

函数作为返回值

//高阶函数——函数作为返回值
function makeFn () {
    let msg = 'Hello function'
    return function () {
        console.log(msg)
    }
}
//调用方法1
const fn = makeFn()
fn()
//无效调用方法,因为makeFn()调用之后返回的是一个函数,需要再加一个括号获得函数返回的东西。
makeFn() 
//调用方法2
makeFn()()

实际测试结果(每次都把另外两种调用方法注释掉):

 

Once函数

//once只有在第一次调用的时候执行
function once (fn) {
    let done = false
        return function () {
        if (!done) {
            done = true
            return fn.apply(this, arguments)
        }
    }
}

let pay = once(function (money) {
    console.log(`支付: ${money} RMB`)
})

pay(5)
pay(5)
pay(5)
pay(5)
pay(5)

 实际测试结果:

使用高阶函数的意义

  • 抽象可以帮我们屏蔽细节,只需要关注与我们的目标
  • 高阶函数是用来抽象通用的问题
  • 使得我们代码更加简洁

3.常用高阶函数模拟

  • forEach

forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数。

注意: forEach() 对于空数组是不会执行回调函数的。

前面其实已经模拟过forEach了,这边只是把上面那段代码copy过来。

//DEMO1
// forEach遍历数列中每一项
function forEach (array, fn) {
    for (let i = 0; i < array.length; i++) {
        fn(array[i])
    }
}
//上课测试样例,需要完成的是打印出数列里的每一项。
let arr = [1, 3, 4, 7, 8]
forEach(arr, function(item){
    console.log(item)
})
  • map

map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。

map() 方法按照原始数组元素顺序依次处理元素。

注意: map() 不会对空数组进行检测。

注意: map() 不会改变原始数组。

本人第一次模拟的

//map
// mao方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。
function map (array, fn) {
    let resultArr = []
    for (let i = 0; i < array.length; i++) {
        resultArr.push(fn(array[i]))
    }
    return resultArr
}
let arr = [1, 2, 3, 4]
let result = map(arr, function(item){
    return item * item;
})
console.log(result)

结果:

返回结果正确是正确,但是看了老师的发现我并没有用到箭头函数。顺便简单补习了一下,为什么要用箭头函数。

箭头函数优点:

1.代码更简洁

2.箭头函数不会绑定this。 或者说箭头函数不会改变this本来的绑定。

JavaScript初学者必看”箭头函数“

https://www.cnblogs.com/fundebug/p/6904753.html

于是跟着跟着老师又把map模拟重新写了一遍。 

//使用箭头函数
var map = (array, fn) => {
    let resultArr = []
    for (let value of array) {
        resultArr.push(fn(value))
    }
    return resultArr
}
let arr = [1, 2, 3, 4]
let result = map(arr, v => v * v);
console.log(result);

运行结果: 

  • filter

filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。

注意: filter() 不会对空数组进行检测。

注意: filter() 不会改变原始数组。

也是copy了一下上面的代码。

//DEMO2
// filter 返回满足fn的数字
function filter (array, fn) {
    let results = []
    for (let i = 0; i < array.length; i++) {
        if (fn(array[i]))             {
            results.push(array[i])
        }
    }
    return results
}
//上课测试样例,需要返回能被2整除的数字
let arr = [1, 3, 4, 7, 8]
let result = filter(arr, function (item){return item % 2 === 0})
console.log(result)
  • every

every() 方法用于检测数组所有元素是否都符合指定条件(通过函数提供)。

every() 方法使用指定函数检测数组中的所有元素:

  • 如果数组中检测到有一个元素不满足,则整个表达式返回 false ,且剩余的元素不会再进行检测。
  • 如果所有元素都满足条件,则返回 true。

注意: every() 不会对空数组进行检测。

注意: every() 不会改变原始数组。

//DEMO6
//every
//我自己写的
var every = (array, fn)=>{
    for (let value of array) {
        if(!fn(value)){
            return false;
        }
    }
    return true;
}
//老师的every代码
const every = (array, fn) => {
    let result = true
    for (const value of array) {
        result = fn(value)
        if (!result) {
            break
        }
    }
    return result
}

let arr = [11 ,12, 14]
let r = every(arr, v => v > 10)
console.log(r)
let r2 = every(arr, v => v > 11)
console.log(r2)

测试结果:

  • some

some() 方法用于检测数组中的元素是否满足指定条件(函数提供)。

some() 方法会依次执行数组的每个元素:

  • 如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测。
  • 如果没有满足条件的元素,则返回false。

注意: some() 不会对空数组进行检测。

注意: some() 不会改变原始数组。

//DEMO7
//some
//我自己写的
var some = (array, fn)=>{
    for (let value of array) {
        if(fn(value)){
            return true;
        }
    }
    return false;
}
//老师的代码
const some = (array, fn) => {
    let result = false
    for (const value of array) {
        result = fn(value)
        if (result) {
            break
        }
    }
    return result
}
let arr = [11 ,12, 14]
let r = some(arr, v => v < 10)
console.log(r)
let r2 = some(arr, v => v < 12)
console.log(r2)
let arr2 = [1,3,5,9]
let r3 = some(arr2, v => v % 2 === 0)
console.log(r3)
let arr3 = [1,3,4,9]
let r4 = some(arr2, v => v % 2 === 0)
console.log(r4)

测试结果:

 

除了上述直接引用外的参考资料:

1.购买的拉勾网《大前端训练营》课程

2.廖雪峰 JavaScript Python Git 教程——函数式编程

https://www.kancloud.cn/wizardforcel/liaoxuefeng/108521

3.MDN First-class Function(头等函数)

https://developer.mozilla.org/zh-CN/docs/Glossary/First-class_Function

以上是关于进阶学习1:函数式编程FP——概念头等函数高阶函数常用高阶函数模拟的主要内容,如果未能解决你的问题,请参考以下文章

进阶学习2:函数式编程FP——闭包纯函数Lodash柯里化

前端学习之函数式编程—函数式编程概念+头等函数

python进阶-- 01 函数式编程

进阶学习4:函数式编程FP——函子FunctorMayBe函子Either函子IO函子FolktalePointer函子Monad

Python进阶学习——函数式编程

Scala:高阶函数隐式转换