JS函数高级技巧之纯函数、高级函数、懒函数和函数组合

纯函数

首先,纯函数要满足两个条件:

  • 给相同的参数返回相同的结果;
  • 不产生任何副作用
js 复制代码
function double(num){
  return num * 2 
}

只要给num的值不变,它返回的结果也不会变,而且这个函数执行的过程中没有对外界造成影响,所以它是一个纯函数。

不是纯函数的两种类型

  1. 执行结果不一样
js 复制代码
const counter = (function () {
    let initValue = 0;
    return function() {
        initValue++;
        return initValue;
    }
})()
console.log(counter()) // 1
console.log(counter()) // 2
console.log(counter()) // 3
  1. 操作了外部变量,产生了副作用
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()函数

懒函数(惰性函数)

视频了解: https://www.bilibili.com/video/BV1Dw411q7ju/?spm_id_from=333.337.search-card.all.click&vd_source=b79be8283df9418cb45941cc0bd583c6

函数体里面会包含各种各样的条件语句,有时候这些条件语句仅仅需要执行一次,比如说我们写单例的时候判断某个对象是否为空,如果为空我们就创建一个对象,那其实我们知道后续只要程序还在运行,这个对象是不可能为空的,但是我们每次使用时都还会判断是否为空,都会执行我们的条件判断。我们可以稍微提升一下性能通过在第一次执行后删除这些条件判断,这样后面就不判断是否为空直接拿来即用了,这就是懒函数。
没有用懒函数

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();
}

这样在第一次执行后,我们用一个新函数重写了之前的函数,后面再执行这个函数的时候我们都会直接返回一个固定的值,这无疑会提高我们代码的性能。所以后续我们遇到一些只用执行一次的条件语句,我们都可以用懒函数来优化它,通过使用一个新函数来覆盖原有的函数来移除条件语句。

函数组合

比如:我们这里有三个函数。分别是:

  1. 乘10
  2. 除2
  3. 转为字符串
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]
相关推荐
zmd-zk4 分钟前
flink学习(2)——wordcount案例
大数据·开发语言·学习·flink
好奇的菜鸟8 分钟前
Go语言中的引用类型:指针与传递机制
开发语言·后端·golang
一条晒干的咸魚8 分钟前
【Web前端】创建我的第一个 Web 表单
服务器·前端·javascript·json·对象·表单
Alive~o.017 分钟前
Go语言进阶&依赖管理
开发语言·后端·golang
花海少爷19 分钟前
第十章 JavaScript的应用课后习题
开发语言·javascript·ecmascript
手握风云-20 分钟前
数据结构(Java版)第二期:包装类和泛型
java·开发语言·数据结构
喵叔哟39 分钟前
重构代码中引入外部方法和引入本地扩展的区别
java·开发语言·重构
尘浮生1 小时前
Java项目实战II基于微信小程序的电影院买票选座系统(开发文档+数据库+源码)
java·开发语言·数据库·微信小程序·小程序·maven·intellij-idea
sinat_384241091 小时前
在有网络连接的机器上打包 electron 及其依赖项,在没有网络连接的机器上安装这些离线包
javascript·arcgis·electron
hopetomorrow1 小时前
学习路之PHP--使用GROUP BY 发生错误 SELECT list is not in GROUP BY clause .......... 解决
开发语言·学习·php