函数式编程(一):前言、高阶函数

前言

什么是函数式编程呢?ennn…,其实就是一种编程思想,和面向对象编程、面向过程编程是一样的,这玩意一开头说一大堆也不好,先来说说为毛要学函数式编程。

因为将来要学习 React 和 vue 以及剖析其源码,避免不了和函数式编程思想接触。

那函数式编程到底有什么好处呢,主要有如下几点:

  1. 不容易产生bug,方便测试和并行处理;
  2. 可以抛弃this,避免被this指向弄晕;
  3. 打包过程中可以更好的利用 tree shaking 过滤无用代码;
  4. 有很多库可以帮助我们进行函数式开发,比如经典的lodash。
  5. 等等…

来看一个小例子:

// 非函数式 
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)

可见,函数式最基本的一个思想,就是把某个过程封装起来,关注结果,弱化过程。

学习函数式编程,我们需要先来学习三个前置知识,分别是:函数是一等公民、高阶函数、闭包。

函数是一等公民

在 JavaScript 中函数就是一个普通的对象 (可以通过 new Function() ),我们可以把函数存储到变量或数组中,它还可以作为另一个函数的参数和返回值,甚至我们可以在程序运行的时候通过 new Function(‘alert(1)’) 来构造一个新的函数。

把函数赋值给变量

// 把函数赋值给变量
let fn = function () {
    console.log('Hello First-class Function')
}
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)
    }
}

// 优化 
const BlogController = {
    index: Views.index,
    show: Views.show,
    create: Db.create,
    update: Db.update,
    destroy: Db.destroy
}

高阶函数

关于高阶函数的大堆概念,这里也不再描述,先只需要知道,高阶函数有两个主要的特点就行,如下:

  1. 函数作为参数:可以把函数作为一个参数传给另一个函数;
  2. 函数作为结果:可以把函数作为另一个函数的结果返回;

函数作为参数

函数作为参数可以让函数非常的灵活,如下代码,传入的fn是一个函数,这个函数里的逻辑完全自定义,这样使得函数的逻辑会变得十分的灵活,这是许多框架底层在用的思想。

foreach示例:

// forEach 
function forEach(array, fn) {
    for (let i = 0; i < array.length; i++) {
        fn(array[i])
    }
}
// 测试
let arr = [1, 3, 5, 7, 8]
forEach(arr, function (item) {
    console.log(item)
})

执行结果:
在这里插入图片描述
filter示例:

// 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
}
// 测试
let arr = [1, 3, 4, 6, 7, 8, 9, 2, 19, 13]
let res = filter(arr, function (item) {
    return item % 2 === 0
})
console.log(res)

执行结果:
在这里插入图片描述
从上面例子可以看出,可以在函数里灵活的写入各种逻辑,然后作为参数传入被执行的另一个函数,这样能使得被执行的函数更为的灵活,这是很多框架底层常见的思想。


函数作为结果

小示例

function makeFn() {
    let msg = 'Hello function'
    return function () {
        console.log(msg)
    }
}
const fn = makeFn()
fn()

执行结果:
在这里插入图片描述
可见,函数作为另一个函数的返回结果,可以获取另一个函数里的局部变量。

再看个具体案例,这个案例模拟了支付功能中避免重复支付的功能:

// once 
function once(fn) {
    let done = false
    return function () {
        if (!done) {
            done = true
            return fn.apply(this, arguments)
        }
    }
}
let pay = once(function (money) {
    console.log(`支付:${money} RMB`)
})
// 只会支付一次 
pay(5)
pay(5)
pay(5)

执行结果:
在这里插入图片描述
虽然调用了3次支付方法,但是这个支付方法只执行了一次。

使用高阶函数的意义

高阶函数是用来抽象通用的问题,抽象可以帮我们屏蔽细节,只需要关注与我们的目标。

看实际代码比较能理解。

如果我们用面向过程的方式来模拟实现一个forEach函数的功能,如下:

let array = [1, 2, 3, 4]
for (let i = 0; i < array.length; i++) {
    console.log(array[i])
}

我们每次要forEach的时候,都要for一次,就很麻烦。

如果用高阶函数,就是这样:

// 高阶高阶函数 
let array = [1, 2, 3, 4]
forEach(array, item => {
    console.log(item)
})

这个还不是很形象,再看上文的那个filter,这么调用:

let arr = [1, 3, 4, 6, 7, 8, 9, 2, 19, 13]
let res = filter(arr, function (item) {
    return item % 2 === 0
})

到这,你可能会说,这不就是数组自带的forEach和filter方法吗?

没错,forEach和filter就是一个高阶函数,它们的第二个参数就是一个函数,这个函数里面可以灵活的写入我们自己的逻辑。

常见的高阶函数有:
forEach
map
filter
every
some
find/findIndex
reduce
sort
……

现在,明白什么是高阶函数了吧?

再用高阶函数来模拟一下map、every和some吧

map

// map方法
const map = (array, fn) => {
    let results = []
    for (const value of array) {
        results.push(fn(value))
    }
    return results
}

// 测试
let arr = [
    { name: '张三', age: 12 },
    { name: '李四', age: 13 },
    { name: '王五', age: 14 },
]
let res = map(arr, item => {
    return item.name
})
console.log(res)    // [ '张三', '李四', '王五' ]

every

// every方法
const every = (array, fn) => {
    let result = true
    for (const value of array) {
        result = fn(value)
        if (!result) {
            break
        }
    } return result
}

// 测试
let arr = [1, 3, 5, 2, 4, 9]
let res = every(arr, item => {
    return item > 0
})
let res2 = every(arr, item => {
    return item > 3
})
console.log(res, res2)  // true false

some

// some方法
const some = (array, fn) => {
    let result = false
    for (const value of array) {
        result = fn(value)
        if (result) { break }
    } return result
}

// 测试
let arr = [1, 3, 5, 2, 4, 9]
let res = some(arr, item => {
    return item === 9
})
let res2 = some(arr, item => {
    return item > 10
})
console.log(res, res2)  // true false

俗话说举一返三,上面举了,ennn… 很多个例子了,应该能返几百几千了。

理解了高阶函数,恭喜,学完本课程大概0.5%的内容了,加油!

文章内容输出来源:拉勾大前端高薪训练营,以上文章中的内容根据老师讲课的语音和代码,结合自己的理解编辑完成。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值