函数式编程(Functional Programming,FP),FP是编程的一种范式,我们常见的编程范式还有面向对象、面向过程。
代码示例:计算两个数的和
// 非函数式(按步骤,面向过程) let num1 = 1 let num2 = 2 let sum = num1 + num2 // 函数式(把过程抽象成函数) function add(n1, n2) { return n1 + n2 } let sum = add(1, 2) 复制代码
一等公民,MDN解释:First-class Function 在JavaScript中函数就是一个普通的对象,既然是对象,那我们就可以把函数存储到变量/数组/对象中,它还可以作为其他函数的参数和返回值使用,也可以再程序运行过程中通过 new Function('alert("xx")')来构造一个新的函数
直接代码演示
// 函数赋值给变量 let fn = function() { console.log('first-class Function') } fn() // 函数存储示例 const BlogController = { index(posts) { return Views.index(posts) }, show(posts) { return Views.show(posts) }, create(attrs) { return Db.create(attrs) }, update(posts, attrs) { return Db.update(posts, attrs) }, destory(posts) { return Db.destory(posts) } } // 示例优化(通过将一个函数/方法赋值给另一个变量) const BlogController = { index: Views.index, show: Views.show, create: Db.create, update: Db.update, destory: Db.destory } 复制代码
高阶函数: Highter-order function
示例:高阶函数-函数作为参数
// 模拟forEach function forEach(array, fn) { if (Object.prototype.toString.call(array) !== '[object Array]' || typeof fn !== "function") return for (let i = 0; i < array.length; i++) { fn(array[i]) } } const forEachArr = [1, 3, 6, 8, 9]; forEach(forEachArr, function(item) { console.log(item) }) 复制代码
// 模拟过滤函数filter function filter(array, fn) { if (Object.prototype.toString.call(array) !== '[object Array]' || typeof fn !== "function") return let res = [] // 存储满足条件的数据 for (let i = 0; i < array.length; i++) { let item = array[i] if (fn(item)) { res.push(item) } } return res } const filterArr = [1, 3, 6, 8 ,9] // 过滤数组中的偶数 const evenNumbers = filter(filterArr, function(item) { return item % 2 === 0 }) 复制代码
以上两个例子我们可以看出:当函数作为参数时调用会更灵活,在使用某个功能时不需要考虑其内部是如何去实现,也达到了功能封装的效果
函数作为返回值,即当我们去调用一个函数的时候,该函数会给我们返回一个Function类型的数据 示例:高阶函数-函数作为返回值
function makeFn() { if (Object.prototype.toString.call(array) !== '[object Array]' || typeof fn !== "function") return let msg = 'Hello function' return function() { console.log(msg) } } // 调用makeFn得到return返回的函数 const fn = makeFn() // 执行fn fn() // 也可以使用连续执行 makeFn()() 复制代码
// 模拟once函数 function once(fn) { if (Object.prototype.toString.call(array) !== '[object Array]' || typeof fn !== "function") return let done = false // 标记当前函数是否被执行 return function() { if (!done) { console.log('>>', arguments) done = true // 返回函数执行结果 return fn.apply(this, arguments) } } } // 模拟支付场景 const pay = once(function(money) { console.log('支付:${money}') }) pay(10) // 执行 // 之后的都不会执行 pay(10) pay(10) 复制代码
举例:比如我们现在需要去遍历一个数组,按照面向过程的方式时我们需要使用一个for循环,定义一个循环变量,判断循环条件等操作;如果此时我们使用高阶函数对遍历这个步骤进行抽象,如上边实现的forEach函数,我们此时只需要知道forEach内部帮我们实现了循环,然后传递数据给forEach。(前边的filter函数也是如此)
// 此处我们使用ES6中的箭头函数来实现 const map = (array, fn) => { if (Object.prototype.toString.call(array) !== '[object Array]' || typeof fn !== "function") return let res = [] // 此处我们使用for循环的替代for of来实现 for (let value of array) { res.push(fn(value)) } return res } const mapArr = [1, 2, 3, 4] // 此处我们来对数组中的元素做平方运算 const squareArr = map(mapArr, value => value * value) console.log(squareArr) 复制代码
const some = (array, fn) => { if (Object.prototype.toString.call(array) !== '[object Array]' || typeof fn !== "function") return let res = false for (let value of array) { res = fn(value) if (res) { break } } return res } const someArr = [1, 3, 4, 5] // 判断数组中是否有偶数 const hasEvenNumber = some(someArr, value => value % 2 == 0) console.log(hasEvenNumber) 复制代码
const every = (array, fn) => { if (Object.prototype.toString.call(array) !== '[object Array]' || typeof fn !== "function") return let res = true // 用于记录数组中的元素是否匹配 for (let value of array) { res = fn(value) if (!res) break } return res } const everyArr = null // 判断数组中每一个元素是否大于10 const moreThenTen = every(everyArr, value => value > 10) console.log(moreThenTen) 复制代码