纯函数
首先,纯函数要满足两个条件:
- 给相同的参数返回相同的结果;
- 不产生任何副作用
js
function double(num){
return num * 2
}
只要给num的值不变,它返回的结果也不会变,而且这个函数执行的过程中没有对外界造成影响,所以它是一个纯函数。
不是纯函数的两种类型
- 执行结果不一样
js
const counter = (function () {
let initValue = 0;
return function() {
initValue++;
return initValue;
}
})()
console.log(counter()) // 1
console.log(counter()) // 2
console.log(counter()) // 3
- 操作了外部变量,产生了副作用
js
let count = 0;
function isYoung(user) {
if (user.age <= 20) {
count++;
return true;
} else {
return false;
}
}
console.log(isYoung({name: 'Tom', age: 25})) // false
console.log(isYoung({name: 'Tom', age: 25})) // false
纯函数的好处
- 纯函数更清晰更容易理解
- 对于纯函数,编译器可以做优化
- 纯函数更易于测试
纯函数的测试不依赖于外部因素,多亏了纯函数的特性,我们给纯函数编写单元测试时只要简单地给个输入然后判断输出是否与预期一致就好了
高阶函数
同样的,高阶函数也有两个条件:
- 接受函数作为参数
- 把函数作为结果返回
比如:map()函数
懒函数(惰性函数)
函数体里面会包含各种各样的条件语句,有时候这些条件语句仅仅需要执行一次,比如说我们写单例的时候判断某个对象是否为空,如果为空我们就创建一个对象,那其实我们知道后续只要程序还在运行,这个对象是不可能为空的,但是我们每次使用时都还会判断是否为空,都会执行我们的条件判断。我们可以稍微提升一下性能通过在第一次执行后删除这些条件判断,这样后面就不判断是否为空直接拿来即用了,这就是懒函数。
没有用懒函数
js
class User {
constructor() {
this.name = 'Tom';
this.age = 25;
}
}
// 不使用懒函数
let instance = null;
function user() {
if (instance !== null) {
return instance;
} else {
instance = new User();
return instance;
}
}
上面的代码在每次执行的时候都会执行条件判断,这边还好,如果我们的条件判断非常复杂,那其实也是一个不小的性能影响,这时候我们就可以使用懒函数的小技巧来优化代码
js
var user = function () {
var instance = new User();
user = function () {
return instance;
}
return user();
}
这样在第一次执行后,我们用一个新函数重写了之前的函数,后面再执行这个函数的时候我们都会直接返回一个固定的值,这无疑会提高我们代码的性能。所以后续我们遇到一些只用执行一次的条件语句,我们都可以用懒函数来优化它,通过使用一个新函数来覆盖原有的函数来移除条件语句。
函数组合
比如:我们这里有三个函数。分别是:
- 乘10
- 除2
- 转为字符串
js
const multiply = (x) => x * 10;
const divide = (x) => x / 2;
const toStr = (x) => x.toString()
当我们直接按顺序执行前三个函数时,我们首先会这么写;
js
const compute = (x) => toStr(divide(multiply(x)))
这边只有三步,所以看起来不复杂,实际情况是如果有更多的操作的话,层层嵌套很难看也容易出错,类似于这样fn3(fn2(fn1(fn0(x))))。为了避免这种情况,把调用层级扁平化,我们可以写一个compose函数专门用来把函数调用组合到一起。
js
const compose = function (fun, f, g) {
return function (x) {
return fun(f(g(x)))
}
}
const computeFun = compose(toStr, divide, multiply)
console.log(computeFun(5)) // "25"
console.log(Object.prototype.toString.call(computeFun(5))) // [object String]
上面这个组合函数只能接收三个函数参数,这边我们编写一个接收任意数量的函数参数
js
// 支持任意数量的函数参数
const composeAny = (...funs) => (x) => funs.reduceRight((acc, fun) => fun(acc), x) // reduceRight: 从右往左遍历
const computeFunAny1 = composeAny(divide, divide, multiply)
console.log(computeFunAny1(8)) // 20
console.log(Object.prototype.toString.call(computeFunAny1(8))) // [object Number]
const computeFunAny2 = composeAny(divide, multiply)
console.log(computeFunAny2(5)) // "25"
console.log(Object.prototype.toString.call(computeFunAny2(5))) // [object Number]
const computeFunAny3 = composeAny(toStr, divide, multiply)
console.log(computeFunAny3(8)) // 40
console.log(Object.prototype.toString.call(computeFunAny3(8))) // [object String]