【函数式编程】闭包与数组函数式编程(JavaScript)

闭包

1. 概念

闭包就是一个内部函数。内部函数是在另一个函数内部的函数。例如:

function outer() {
    function inner() {
    }
}

闭包有3个可访问的作用域

  • 在它自身声明之内声明的变量

    function outer1() {
    	function inner() {
    		let a = 5
    		console.log(a)
    	}
    	inner()
    }
    
  • 对全局变量的访问

    let global_ = "global"
    function outer2() {
    	function inner() {
    		console.log(global_)
    	}
    	inner()
    }
    
  • 对外部函数变量的访问

    function outer3() {
    	let outer = "outer"
    	function inner() {
    		console.log(outer)
    	}
    	inner()
    }
    
    function outer4(x) {
    	function inner() {
    		console.log(x)
    	}
    	inner()
    }
    

闭包可以记住它的上下文

var fn = (arg) => {
	let outer = "Visible"
	let innerFn = () => {
		console.log(outer)
		console.log(arg)
	}
	return innerFn
}

var closureFn = fn(5)	// 返回了innerFn 并记住了arg, outer的值
closureFn()

2. 应用

tap函数

const tap = (value) => (fn) => (
	typeof(fn) === 'function' && fn(value), console.log(value))

tap函数接受一个value并返回一个包含value的闭包函数

tap("fun")((it) => console.log("value is", it))
> value is fun
> fun

forEach([1, 2, 3], (a) => tap(a)(() => {console.log(a)}))

unary函数

> [1,2,3].map((a) => {return a * a})
[ 1, 4, 9 ]

> ['1','2','3'].map(parseInt)
[ 1, NaN, NaN ]

unary的任务是接受一个给定的多参数函数,并把它转换为一个只接受一个参数的函数

> const unary = (fn) => fn.length === 1 ? fn : (arg) => fn(arg)
> ['1','2','3'].map(unary(parseInt))
[ 1, 2, 3 ]
> parseInt.length
> 2

补充:函数的length

> var fn = (x, y, z) => {console.log(x + y + z)}
> fn(1,2,3)
6
> fn.length
3

once函数

允许开发者只运行一次给定的函数

const once = (fn) => {
	let done = false;
	return function() {
		return done ? undefined : ((done=true), fn.apply(this, arguments))
	}
}
var doPayment = once(() => {
	console.log("Payment is done")
})

doPayment()
doPayment()  // undefined

memoized函数

函数是一一对应的,因此可以记住运行结果,避免重复运算,直接查表

const memoized = (fn) => {
	const lookupTable = {}
	return (arg) => lookupTable[arg] || (lookupTable[arg] = fn(arg))
}
let fastFactorial = memoized((n) => {
	if (n === 0) return 1
	return n * fastFactorial(n - 1)  
})

console.log(fastFactorial(5))
console.log(fastFactorial(3))

投影函数(Projecting Function):把函数应用于一个值并创建一个新值的过程

数组的函数式编程

1. map

map函数定义

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

var results = map([1, 2, 3], (x) => x * x)
forEach(results, (data) => (console.log(data)))

把函数封装到arrayUtils对象中

const arrayUtils = {
	map: map
}

export {arrayUtils}

// -----------------


import {arrayUtils, forEach} from '../lib/chap05-functional.js'

forEach(arrayUtils.map([1, 2, 3], (x) => x * x * x), (data) => {console.log(data)})

如何用map函数获取书名和作者

let apressBooks = [
  {
    "id": 111,
    "title": "C# 6.0",
    "author": "ANDREW TROELSEN",
    "rating": [4.7],
    "reviews": [{good : 4 , excellent : 12}]
  },
  {
    "id": 222,
    "title": "Efficient Learning Machines",
    "author": "Rahul Khanna",
    "rating": [4.5],
    "reviews": []
  },
  {
    "id": 333,
    "title": "Pro AngularJS",
    "author": "Adam Freeman",
    "rating": [4.0],
    "reviews": []
  },
  {
    "id": 444,
    "title": "Pro ASP.NET",
    "author": "Adam Freeman",
    "rating": [4.2],
    "reviews": [{good : 14 , excellent : 12}]
  }
];
var title_author = arrayUtils.map(apressBooks, (book) => {
	return {title: book.title, author: book.author}
})
forEach(title_author, (data) => console.log(data))

2. filter

filter函数的定义

const filter = (array, fn) => {
	let results = []
	for (const value of array) {
		(fn(value)) ? results.push(value) : undefined
	}
	return results
}

const arrayUtils = {
	map: map,
	filter: filter
}

筛选rating>4.5的书籍

var rating = arrayUtils.filter(apressBooks, (book) => book.rating[0] > 4.5)
forEach(rating, (data) => console.log(data))

从apressBooks中获取含有title和author对象且评级高于4.5的对象

let goodRatingBooks = arrayUtils.map(arrayUtils.filter(apressBooks, (book) => book.rating[0] > 4.5), (book) => {
  return {
    title: book.title,
    author: book.author
  }
})
forEach(goodRatingBooks, (data) => console.log(data))

3. flatten

push.apply()方法

> var arr1 = [1, 2]
undefined
> var arr2 = [3, 4, 5]
undefined
> arr2.push.apply(arr2, arr1)
5
> arr2
[ 3, 4, 5, 1, 2 ]

concatAll(也就是flatten)

把所有嵌套数组连接到一个数组中。把所有的bookDetails碾平。

let apressBooks3 = [
  {
    name : "beginners",
    bookDetails : [
      {
        "id": 111,
        "title": "C# 6.0",
        "author": "ANDREW TROELSEN",
        "rating": [4.7]
      },
      {
        "id": 222,
        "title": "Efficient Learning Machines",
        "author": "Rahul Khanna",
        "rating": [4.5],
        "reviews": []
      }
    ]
  },
  {
      name : "pro",
      bookDetails : [
      {
        "id": 333,
        "title": "Pro AngularJS",
        "author": "Adam Freeman",
        "rating": [4.0],
        "reviews": []
      },
      {
        "id": 444,
        "title": "Pro ASP.NET",
        "author": "Adam Freeman",
        "rating": [4.2]
      }
    ]
  }
];

concatAll函数定义

concatAll的主要目的是将嵌套数组转换为非嵌套的单一数组。

const concatAll = (array) => {
	let results = []
	for (const value of array) {
		results.push.apply(results, value)
	}
	return results
}

const arrayUtils = {
	map: map,
	filter: filter,
	concatAll: concatAll
}
var bookDetails = arrayUtils.concatAll(arrayUtils.map(apressBooks3, (book) => {
  	return book.bookDetails
  })
)
forEach(bookDetails, (data) => console.log(data))

选出rating>4.5的书籍

var goodRatingCriteria = (book) => book.rating[0] > 4.5
var bookInfo = arrayUtils.filter(
                arrayUtils.concatAll(
                  arrayUtils.map(apressBooks3, (book) => {
                    return book.bookDetails
                  })
                )
              , goodRatingCriteria)

forEach(bookInfo, (data) => console.log(data))

4. Reduce

基于加法求和的reduce函数定义

const reduce = (array, fn) => {
	let accumulator = 0
	for (const value of array) {
		accumulator = fn(accumulator, value)
	}
	return [accumulator]
}
let useless = [2, 5, 6, 1, 10]
var sum = arrayUtils.reduce(useless, (acc, val) => acc + val)
console.log(sum)

问题:如果是乘法呢,accumulator=0,不就失效了吗?

改进的Reduce函数

const reduce = (array, fn, initialValue) => {
	let accumulator;
	if (initialValue != undefined) {
		accumulator = initialValue
		for (const value of array) {
			accumulator = fn(accumulator, value)
		}
	} else {
		accumulator = array[0]
		for (let i = 1; i < array.length; i++) {
			accumulator = fn(accumulator, array[i])
		}
	}
	return [accumulator]
}
let useless = [2, 5, 6, 1, 10]
var sum = arrayUtils.reduce(useless, (acc, val) => acc + val)
console.log(sum)
var product = arrayUtils.reduce(useless, (acc, val) => acc * val, 2)
console.log(product)

统计reviewDetails评价为good和excellent的数量

let reviewDetails = [
  {
    "id": 111,
    "reviews": [{good : 4 , excellent : 12}]
  },
  {
    "id" : 222,
    "reviews" : []
  },
  {
    "id" : 333,
    "reviews" : []
  },
  {
    "id" : 444,
    "reviews": [{good : 14 , excellent : 12}]
  }
]

let reviews = arrayUtils.concatAll(arrayUtils.map(reviewDetails, (book) => {
    return book.reviews
  })
)
forEach(reviews, (data) => console.log(data))

let statistic = arrayUtils.reduce(reviews, (acc, review) => {
    let goodReviews = review.good != undefined ? review.good : 0   
    let excellentReviews = review.excellent != undefined ? review.excellent : 0
    return {good: acc.good + goodReviews, excellent: acc.excellent + excellentReviews}
  }, 
{good: 0, excellent: 0}) 
forEach(statistic, (data) => console.log(data))

5. zip

zip函数的任务是合并两个给定的数组

const zip = (leftArr, rightArr, fn) => {
	let index, results = []
	for (index = 0; index < Math.min(leftArr.length, rightArr.length); index++) {
		results.push(fn(leftArr[index], rightArr[index]));
	}
	return results
}

forEach(arrayUtils.zip([1, 2, 3], [4, 5, 6], (x, y) => x + y), (data) => console.log(data))
forEach(arrayUtils.zip([1, 2, 3], [4, 5, 6, 7], (x, y) => x * y), (data) => console.log(data))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值