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、函数作为“一等公民”

函数作为“一等公民”主要有下方几个表现:

  1. 变量类型可以是函数
  2. 函数的参数可以是函数
  3. 值(literal)可以是函数
  4. 对象的字段可以是函数
  5. 函数的返回值可以是函数

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、函数式编程在微信小程序中应用的例子

使用函数式编程的方式,下图红色方框中的变量可以只声明在函数内部,不需要全局声明。对于代码的维护有很大的好处

typescript 封装 Axios_函数式编程