[TOC ] >[success] # 了解函数式编程需要知道的 ~~~ 1.函数是一等公民 2.高阶函数 3.闭包 ~~~ >[info] ## 函数是一等公民在js中 [mdn讲解](https://developer.mozilla.org/zh-CN/docs/Glossary/First-class_Function) ~~~ 1.'函数可以存储在变量中':即使你的函数有自己的函数名称,你仍然可以用这个变量名去调用它。 给函数命名只会方便你调试,但不会影响我们调用它。 2.'函数作为参数':我们把一个函数作为参数传递给另外一个函数,那么我们就称这个函数为回调函数 3.'函数作为返回值':一个返回另外一个函数的函数被称为高阶函数 4.在 JavaScript 中函数就是一个普通的对象 (可以通过 new Function() ),我们可以把函数存储到变量 数组中,它还可以作为另一个函数的参数和返回值,甚至我们可以在程序运行的时候通过 new Function('alert(1)') 来构造一个新的函数 ~~~ >[danger] ##### 把函数赋值给变量 ~~~ // 把函数赋值给变量 let fn = function () { console.log('Hello First-class Function') } fn() ~~~ * 案例二 ~~~ 1.一个函数包裹另一个函数,并且形式也相同的时候,可以认为是一样的函数 2.下面为例BlogController.index(post) 和 Views.show(post)。调用后传参的形式都是一样, 这里可以认为两个是一个函数,利用可以把函数赋值的这一个特性,可以吧View.index 赋值 给'BlogController'对象的index 上,注意赋值的是'View.index' 不是它的调用 ~~~ ~~~ 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 } ~~~ >[danger] ##### 高阶函数 ~~~ 1.高阶函数就是利用js 函数是一等公民的中的两点 1.1.可以把函数作为参数传递给另一个函数 1.2.可以把函数作为另一个函数的返回结果 2.高阶函数的意义 2.1.抽象可以帮我们屏蔽细节,只需要关注我们的目标 2.2.高阶函数是用来抽象通用的问题 ~~~ * 函数作为参数的例子 * 第一印象写法 ~~~ // 实现一个类似fittler 功能 // 最初的代码思路 function fittler1(array,rules){ const result = [] for(let item of array){ if(rules){ result.push(item) } } result } // 使用过滤出所有 大于5的数 上面封装的代码根本不能用 // 要不就是在if逻辑哪里写死后续不能扩展 ~~~ * 正确做法 ~~~ 1.以filter 方法为例,要做的是过滤数组一件事,将这个事抽象出一个通用的解决问题的函数,在使用的 时候我们就不在关心封装的内部,只要关心调用这个方法即可,并且通过调用的名字一目了然知道做什么 用 ~~~ ~~~ 作为返回值 // forEach function forEach (array, fn) { for (let i = 0; i < array.length; i++) { fn(array[i]) } } // filter function filter (array, fn) { let results = [] for (let i = 0; i < array.length; i++) { if (fn(array[i])) { results.push(array[i]) } } return results } // 使用 filter([5,1,8,2],(item)=>item>5) ~~~ * 函数作为返回值 ~~~ 1.有个函数只执行一次,场景用户秒杀只能秒杀一次,多次执行也只能执行一次 ~~~ * 这种需求我的第一印象做法 ~~~ 1.下面的代码针对性质太强,可服用性质也不好 ~~~ ~~~ // 这种需求我的第一印象 的写法 let flag = false // 做一个开关 function once1() { if (!flag) { flag = true // .... 各种处理的逻辑写死 console.log('1') } } // 调用 once1() once1() // 不会打印 1的 ~~~ * 另一种思路 推荐 ~~~ 1.这里套用高阶函数的意义来分析,将只调用一次的个处理抽象出来一个通用问,在使用的时候 不关心内部,只关心调用的once使用不关心内部实现 ~~~ ~~~ // once 只执行一次,场景用户秒杀只能秒杀一次,多次执行也只能执行一次 function once(fn) { let done = false return function () { if (!done) { done = true // 要考虑回调的函数如果有返回值怎么办 return fn.apply(this, arguments) } } } let callback = once(() => { console.log(12) }) callback() // 执行了一次 callback() ~~~ >[info] ## js 中常见的高阶函数 ~~~ forEach map filter every some find/findIndex reduce sort …… ~~~ >[danger] ##### 实现一个和数组map同功能的方法 ~~~ // map 方法会给原数组中的每个元素都按顺序调用一次 // callback 函数。callback 每次执行后的返回值(包括 undefined)组合起来形成一个新数组 const list = [1, 5, 6, 7, 8] let newList = list.map(item => item * 2) console.log(newList) // [ 2, 10, 12, 14, 16 ] function map(array, fn) { // 返回的是新数组 const newArray = [] for (let item of array) { // 每一个项进入回调后的返回值,回调的参数是每一项 newArray.push(fn(item)) } return newArray } newList = map(list, (item) => item * 2) console.log(newList) // [ 2, 10, 12, 14, 16 ] ~~~ >[danger] ##### 实现一个和数组same同功能的方法 ~~~ // 方法测试数组中是不是至少有1个元素通过了被提供的函数测试。 // 它返回的是一个Boolean类型的值。 const list = [1, 5, 6, 7, 8] let flag = list.some(item => item === 1) console.log(flag) // true function some(array, fn) { let flag = false for (let item of array) { flag = fn(item) if (flag) { break } } return flag } flag = some(list, (item) => item === 1) console.log(flag) // true ~~~ >[danger] ##### 实现一个和数组every同功能的方法 ~~~ // 方法测试数组中每个元素通过了被提供的函数测试。 // 它返回的是一个Boolean类型的值。 const list = [1, 5, 6, 7, 8] let flag = list.every(item => item === 1) console.log(flag) // false function every(array, fn) { let flag = false for (let item of array) { flag = fn(item) if (!flag) { break } } return flag } flag = every(list, (item) => item === 1) console.log(flag) // false ~~~