文章目录
闭包
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))