什么是函数式编程?编程
函数式编程的前置知识segmentfault
高阶函数数组
闭包闭包
闭包的案例app
ps:对总体不太了解的先看有线函数式编程整体设计,再阅读下面的内容~~~
函数式编程是一个很是古老的概念。函数式编程
函数式编程,缩写FP,是一种编程范式,也是一种编程风格,和面向对象是并列的关系。函数式编程咱们能够认为是一种思惟模式,加上实现方法。其思惟方式就是把现实世界事物和事物之间的联系抽象到程序世界(是对运算过程进行抽象
)函数
常据说的编程范式还有面向过程编程(按照步骤来实现)、面向对象编程(把现实中的事物抽象成类和对象,经过封装、继承和多态来演示不一样事物之间的联系)。post
从思惟方式上来讲 面向对象编程是对事物的抽象,而函数式编程是对运算过程的抽象测试
// 非函数式 let num1 = 2 let num2 = 3 let sum = num1 + num2 console.log(sum) // 函数式 function add(n1, n2) { return n1 + n2 } let sum = add(2, 3) console.log(sum)
在JS中函数就是一个普通的对象,咱们能够把函数存储到变量/数组中,它还能够做为另外一个函数的参数和返回值,甚至咱们能够在程序运行的时候经过new Function('alert(1)')
来构造一个新的函数。优化
// 把函数赋值给变量 let fn = function () { console.log("hi") } 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) } } // 优化 赋值的是Views的index方法,不是方法的调用 const BlogController = { index: Views.index, show: Views.show, create: Db.create, update: Db.update, destroy: Db.destroy }
下面两个特性在高阶函数中会有详细说明
高阶函数(Higher-order function)
// forEach // 定义一个遍历数组的并对每一项作处理的函数,第一个函数是一个数组,第二个参数是一个函数。 function forEach (array, fn) { for (let i = 0; i < array.length; i++) { fn(array[i]) } } // test let arr = [1, 2, 3] forEach(arr, item => { item = item * 2 console.log(item) // 2 4 6 })
// 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 } // test let arr = [1, 3, 4, 7, 8] let result = filter(arr, item => item % 2 === 0) console.log(result) // [4, 8]
// 一个函数返回另外一个函数 function makeFn () { let msg = 'Hello function' return function () { console.log(msg) } } // test // 第一种调用方式 const fn = makeFn() fn() //Hello function // 第二种调用方式 makeFn()()///Hello function
// once // 让函数只执行一次 function once(fn) { let done = false return function() { // 判断值有没有被执行,若是是false表示没有执行,若是是true表示已经执行过了,没必要再执行 if(!done) { done = true // 调用fn,当前this直接传递过来,第二个参数是把fn的参数传递给return的函数 return fn.apply(this, arguments) } } } // test let pay = once(function (money) { console.log(`支付:${money} RMB`) }) pay(5) //支付:5 RMB pay(5) pay(5) pay(5) pay(5)
有一个通用的特色,须要一个函数做为参数。
const map = (array, fn) => { let results = [] for (const value of array) { results.push(fn(value)) } return results } // test let arr = [1, 2, 3, 4] arr = map(arr, v => v * v) console.log(arr) //
const every = (array, fn) => { let result = true for (const value of array) { result = fn(value) // 若是有一个元素不知足就直接跳出循环 if (!result) { break } } return result } // test let arr = [11, 12, 14] let r = every(arr, v => v > 10) console.log(r) // false r = every(arr, v => v > 12) console.log(r) // false
const some = (array, fn) => { let result = false for (const value of array) { result = fn(value) // 若是有一个元素不知足就直接跳出循环 if (result) { break } } return result } // test let arr = [1, 3, 4, 9] let arr1 = [1, 3, 5, 9] let r = some(arr, v => v % 2 === 0) console.log(r) // true r = some(arr1, v => v % 2 === 0) console.log(r) // false
闭包:函数和其周围的状态(词法环境)的引用捆绑在一块儿造成闭包
在上面函数做为返回值的过程当中,其实咱们就用到了闭包,下面进行语法演示:
function makeFn () { let msg = 'Hello function' } // 正常状况下,执行完makeFn,里面的变量msg会释放掉 // 可是下面的状况 function makeFn () { let msg = 'Hello function' return function () { console.log(msg) } } // 在上面函数中,返回了一个函数,并且在函数中还访问了原来函数内部的成员,就能够称为闭包 const fn = makeFn() fn() // fn为外部函数,当外部函数对内部成员有引用的时候,那么内部的成员msg就不能被释放。当咱们调用fn的时候,咱们就会访问到msg。 //注意的点: //一、咱们能够在另外一个做用域调用makeFn的内部函数 //二、当咱们调用内部函数的时候咱们能够访问到内部成员
把函数内部成员的做用范围延长
函数在执行的时候会放到一个执行栈上,当函数执行完毕以后会从执行栈上移除。可是堆上的做用域成员由于被外部引用不能释放,所以内部函数依然能够访问外部函数的成员。
/解读:函数执行的时候在执行栈上,执行完毕以后从执行栈上移除,内部成员的内存被释放。可是在函数执行完毕移除以后,释放内存的时候,若是外部有引用,则内部成员的内存不能被释放。/
Math.pow(4, 2) Math.pow(5, 2) // 后面的二次方三次方不少次重复,下面要写一个二次方三次方的函数 function makePower (power) { return function (number) { return Math.pow(number, power) } } // 求平方 let power2 = makePower(2) let power3 = makePower(3) console.log(power2(4)) // 16 console.log(power2(5)) // 25 console.log(power3(4)) // 64
调试台的案例演示
// 假设计算员工工资的函数第一个函数传基本工资,第二个参数传绩效工资 // getSalary(12000, 2000) // getSalary(15000, 3000) // getSalary(15000, 4000) // 不一样级别的员工基本工资是同样的,因此咱们将基本工资提取出来,以后只须要加上绩效工资 function makeSalary (base) { return function (performance) { return base + performance } } let salaryLevel1 = makeSalary(12000) let salaryLevel2 = makeSalary(15000) console.log(salaryLevel1(2000)) //14000 console.log(salaryLevel2(3000)) //18000 console.log(salaryLevel2(4000)) //19000