typescript:3、typescript函数式编程
- 1、函数式编程的风格
- 1.1、函数作为“一等公民”
- 1.1.1、变量类型可以是函数、值可以是一个函数
- 1.1.2、函数的参数可以是函数
- 1.1.3、对象的字段可以是函数
- 1.1.4、函数的返回值可以是函数
- 1.2、高阶函数
- 1.3、部分应用函数
- 2、无副作用
- 3、引用透明性
- 4、惰性计算(lazy evaluation)
- 5、函数式编程在微信小程序中应用的例子
javascript是一种函数式编程风格的语言
1、函数式编程的风格
1.1、函数作为“一等公民”
函数作为“一等公民”主要有下方几个表现:
- 变量类型可以是函数
- 函数的参数可以是函数
- 值(literal)可以是函数
- 对象的字段可以是函数
- 函数的返回值可以是函数
1.1.1、变量类型可以是函数、值可以是一个函数
const compareNumber = function(a:number, b:number):number{
return a - b
}
1.1.2、函数的参数可以是函数
const a = [5,2,6,7,41,2,5,7]
let compareNumber = function(a:number, b:number){
return a-b
}
a.sort(compareNumber)
console.log(a)
箭头函数/lamda表达式 写法
const a = [5,2,6,7,41,2,5,7]
//下方定义的也是一个函数
let compareNumber = (a:number, b:number) => a - b
a.sort(compareNumber)
console.log(a)
1.1.3、对象的字段可以是函数
const emp1 = {
name : 'john',
salary : 8000,
//increaseSalary是对象的字段
increaseSalary: function(p:number){
this.salary *= p
}
}
emp1.increaseSalary(1.1)
console.log(emp1)
1.1.4、函数的返回值可以是函数
function createComparer(p:{smallerFirst: boolean}){
if( p.smallerFirst){
return (a:number, b:number) => a - b
}else{
return (a:number, b:number) => b - a
}
}
let a = [5,2,3,1,78,21,11,33]
a.sort( createComparer({smallerFirst:true}))
console.log(a)
1.2、高阶函数
//在comp的基础上做了一层封装,如果comp是某个插件的函数,想在调用该函数之前执行一些操作的话可以这样做。
function loggingComparer( comp:(a:number, b:number) => number ){
return (a:number, b:number) => {
console.log('comparing', a, b)
return comp(a,b)
}
}
function createComparer(p: {smallerFirst: boolean}){
if( p.smallerFirst){
return (a:number, b:number) => a -b
}else{
return (a:number, b:number) => b - a
}
}
let a = [5,2,1,6,8,10,5,25,16,23,11]
const comp = createComparer({smallerFirst: true})
a.sort(loggingComparer(comp))
console.log(a)
1.3、部分应用函数
下方代码中,若isGoodNumber函数的参数与我们的不一致,但是我们又需要使用它,那么就可以(v) => isGoodNumber( GOOD_FACTOR, v)
使用定义一个匿名函数的方式来调用到isGoodNumber函数
function isGoodNumber(goodFactor:number, v:number){
return v % goodFactor === 0
}
function filterArray(a:number[], f:(v:number) => boolean){
return a.filter(f)
}
function filterArray2(a:number[], f:(v:number,v2:number) => boolean){
return a.filter(f)
}
const GOOD_FACTOR = 2
const a = [1,2,3,4,5,6,7,8,9]
console.log(filterArray( a, (v) => isGoodNumber(GOOD_FACTOR, v)))
部分应用函数显示的例子
真实产品中还是采用上面那段代码,不会像下面这样写
const GOOD_FACTOR = 2
const a = [1,2,3,4,5,6,7,8,9]
function isGoodNumber(goodFactor:number, v:number){
return v % goodFactor === 0
}
function filterArray( a:number[], f:(v:number) => boolean){
return a.filter(f)
}
//假设isGoodNumber为某个插件的函数,希望在filterArray中调用isGoodNumber函数
//具体的实现流程如下,但是一般不会写的这么麻烦,下方这样写代码量太多了
function partiallyApply(
f: (a: number, b: number) => boolean,
a: number
){
return (b: number) => {
return f(a,b)
}
}
console.log( filterArray( a, partiallyApply(isGoodNumber, GOOD_FACTOR)))
2、无副作用
下方代码中引用了全局变量compCount来统计函数比较的次数;
若需要再次执行的时候,就需要将全局变量置空;
很容易出现忘记将全局变量置空的情况;
此时使用闭包则能够很轻松的解决这个问题;
类似的场景还有使用全局变量来保存按钮的状态;
let compCount = 0
function loggingComparer( comp: (a: number, b:number) => number ){
return (a: number, b:number) => {
//引用全局变量compCount
compCount++
return comp(a, b)
}
}
function createComparer(p: {smallerFirst: boolean}){
if( p.smallerFirst){
return (a: number, b:number) => a - b
}else{
return (a: number, b:number) => b - a
}
}
let a = [5,2,1,6,7,10,5,25,16,23,11]
const comp = createComparer({smallerFirst: true})
a.sort( loggingComparer(comp))
//若需要再次执行的时候,就需要将全局变量置空
compCount = 0
a.sort(loggingComparer(comp))
console.log(a)
console.log('compare count', compCount)
使用闭包来解决全局变量忘记置空的问题
function loggingComparer(
logger: (a: number, b: number) => void,
comp: (a: number, b:number) => number
){
return (a: number, b: number) => {
logger(a, b)
return comp(a, b)
}
}
function createComparer( p: {smallerFirst: boolean}) {
if( p.smallerFirst) {
return (a: number, b:number) => a - b
}else{
return (a: number, b:number) => b - a
}
}
function processArray(a: number[]){
//下方compCount和logger行成了一个闭包,compCount的生命周期延长了
let compCount = 0
const logger = (a: number, b:number) => {
compCount++
}
const comp = createComparer({smallerFirst: true})
a.sort(loggingComparer(logger, comp))
return compCount
}
let a = [5,2,1,6,8,10,5,25,16,23,11]
const compCount1 = processArray(a)
const compCount2 = processArray(a)
console.log(a)
console.log('comparer count', compCount1, compCount2)
3、引用透明性
function add(a:number, b:number):number{
return a + b
}
//下方两行代码从引用透明性的角度来讲,他们等价
//因为add(2,3)返回5,那么直接使用5也是一样的。
console.log( add(2, 3))
console.log( 5)
上方代码是等价的。
但是下方代码在引用透明性的角度来说就不等价了,因为下方add函数中打印了一行内容,所以说下方的add函数不具有引用透明性
function add(a:number, b:number):number{
console.log(2,3)
return a + b
}
//下方两行代码从引用透明性的角度来讲,他们等价
//因为add(2,3)返回5,那么直接使用5也是一样的。
console.log( add(2, 3))
console.log( 5)
4、惰性计算(lazy evaluation)
function add(a:number, b:number) {
return a + b
}
//惰性计算指的是如下方的3+4只有在真正调用时才会算表达式的值,也就是7
//javascript不支持惰性计算,所以在调用add函数时就会将3+4的值作为参数传进add函数的参数中
add( 2, 3+4)
5、函数式编程在微信小程序中应用的例子
使用函数式编程的方式,下图红色方框中的变量可以只声明在函数内部,不需要全局声明。对于代码的维护有很大的好处