6 Higher-Order Function
//self-contained 自包含的 自解释的
var total = 0, count = 1
while (count <= 10) {
total += count
count += 1
}
console.log(total)
//依赖两个外部的函数
console.log(sum(range(1, 10)))
function range(start, end) {
var result = []
for (var i = start; i <= end; i++) {
result.push(i)
}
return result
}
Abstraction
-
抽象隐藏细节
-
高阶函数核心函数可以被传递
function f(x) {
var a = 8
x()
console.log(x + 'xxx')
}
var a = 2
f(function () {
console.log(a)
})
// 括号不代表作用域 function 的作用域是全局 不能读到a = 8
// 函数在哪里声明 就在那里找 打印出
// 2
// function () {
// console.log(a)
// } xxx
// 函数和字符串拼接 打印出源码加字符串
function f() {
return function add(a, b) {
return a + b
}
}
var g = f()
// 右面运行f 返回一个函数add g指向函数add
console.log(g(1, 2))
//3
Abstracting array traversal
//兜圈子的方式 遍历数组
var array = [1, 2, 3]
for (var i = 0; i < array.length; i++) {
var current = array[i]
console.log(current)
}
// 把遍历数组抽象
function logEach(array) {
for (var i = 0; i < array.length; i++) {
console.log(array[i])
}
}
// 如果想对数组做其他的事情而不是打印
function forEach(array, action) {
for (var i = 0; i < array.length; i++) {
action(array[i])
}
}
var sum = 0
forEach([1, 2, 3, 4], function (value) {
sum = sum + value
})
// function里面的value作为形参 为了让function能访问到array[i]
console.log(sum)
//10
var sum = 0
function forEach(array, action) {
for (var i = 0; i < array.length; i++) {
action(array[i])
}
}
function addToSum(value) {
sum = sum + value
}
forEach([1, 2, 3, 4], addToSum)
console.log(sum)
数组的forEach 方法
[1, 2, 3, 4].forEach(function (val) {
console.log(val)
})
// 1 2 3 4
[1, 2, 3, 4].forEach(function (val, index, thearray) {
console.log(val, index, thearray)
})
//自己实现
function forEach(array, action) {
for (let i = 0; i < array.length; i++) {
action(array[i], i, array)
}
}
forEach([1, 2, 3, 4], console.log)
//数组或对象使用
function forEach(obj, action) {
if (Array.isArray(obj)) {
for (var i = 0; i < Array.length; i++) {
action(ary[i], i, ary)
}
} else {
for (var key in obj) {
action(obj[key], key, obj)
}
}
}
forEach({
a: 1,
b: 2,
c: 3,
}, function (val, propName) {
console.log(val, propName)
})
// 1 a
// 2 b
// 3 c

Higher-order functions
- 当一个函数操作其他函数时,(不管那他们当参数,还是返回一个函数)。这个函数就被称为高阶函数。
- 高阶函数对动作进行抽象,普通函数对数据进行抽象
- 高阶函数有几种形式
- 创建一个返回新函数的函数
//返回一个判断其唯一参数是否大于n的函数
function greaterThan(n) {
return function (m) { return m > n }
}
var greaterThan5 = greaterThan(5)
var greaterThan10 = greaterThan(10)
console.log(greaterThan10(11))
//true
-
greaterThan5 == greaterThan10 false
-
greaterThan5.toString() === greaterThan10.toString() true 源代码相同

- 用一个函数改变其他函数的行为
function noisy(f) {
return function (arg) {
console.log('valling with', arg)
var val = f(arg)
console.log('called with', arg, '- got', val)
return val
}
}
//为函数f添加一点噪音
// 返回一个函数 跟原来对比 参数相同 返回的值相同 但是多了两个console.log
console.log(noisy(Boolean)(0))
// valling with 0
// called with,0,-got,false
// false
-
可以通过函数提供新型的控制流
function unless(test, then) { if (!test) then() } function repeat(times, body) { for (var i = 0; i < times; i++) { body(i) } } repeat(3, function (n) { unless(n % 2, function () { console.log(n, 'is even') }) }) // 0 is even // 2 is evenlexical scoping rules 词法作用域规则
- 内部函数定义的变量并不会在外面被访问
Passing alone arguments
-
可变参数
-
没有...时 es5
function f() { var args = Array.from(arguments) //把f所有参数变成数组 return g.apply(null,args) //apply 把args的每一个元素 抽出来 作为参数 }function f(a, b, c, d) { console.log(a + b + c + d) } f(1, 2, 3, 4) // 此时 a=1 b=2 c=3 d=4 f(...[5, 1, 8]) //...把数组展开 a=5 b=1 c=8 d=undefined f.apply(null, [1, 2, 3, 4, 5, 6, 7]) //传七个参数 a=1 b=2 c=3 d=4 可通过argument用后面的参数function transparentWrapping(f) { return function(){ return f.apply(null.arguments) } } // 一个不多一个不少的传递所有参数 也仅仅只有这些参数
JSON
[
{ "name": "Emma", "sex": "f", "born": "1876", "died": "1956", "father": "Petrus", "mother": "Sophia",},
{ "name": "Carolus", "sex": "m", "born": "1832", "died": "1905", "father": "Carel", "mother": "Maria", },...
and so on
]
- 所有属性必须双引号包裹,而且只能简单数据表达式(直接量),不能有运算。双引号内部不能出现明文tab符,不能出现任意多余符号,没有undefined和NaN
var jsonString = `
[{
"name": "Lily",
"born": "1890",
"died": "1965"
},{
"name": "Jim",
"born": "1790",
"died": "1865"
}]
`
var ary = JSON.parse(jsonString)
//JSON 转化成数组
console.log(ary)
// [
// { name: 'Lily', born: '1890', died: '1965' },
// { name: 'Jim', born: '1790', died: '1865' }
// ]
console.log(JSON.stringify(ary))
//字符串化
// [{ "name": "Lily", "born": "1890", "died": "1965" }, { "name": "Jim", "born": "1790", "died": "1865" }]
JSON.stringify(ary, null, 2)
//缩进两个空格 遇到} ] , 就回车
console.log(JSON.parse(`{"a":"1"}`))
//{ a: '1' }
-
JSON.stringify 把js的值转换为JSON编码的字符串 序列化 seriallize
-
JSON.parse 把JSON的string转化为js的数据 反序列化
Filtering an array
function filter(ary, test) {
var result = []
// for (var i = 0; i < ary.length; i++) {
// if (test(ary[i])) {
// result.push(ary[i])
// }
// }
ary.forEach(function (p) {
if (test(p)) {
result.push(p)
}
})
return result
}
console.log(
filter(ancestry, function (p) {
if (p.sex == 'f') {
return true
}
})
)
//filter的使用
console.log(ancestry.filter(function(person){
return person.father == "Carel Haverbeke"
}))
Transforming with map
function map(ary, mapper) {
var result = []
for (var i = 0; i < ary.length; i++) {
result.push(mapper(ary[i]), i, ary)
}
return result
}
ancestry.map(function (p, idx) {
return p.name
})
//fliter返回数组 map返回值
//链式调用
ancestry.filter(function (p, idx) {
return p.died - p.born > 50
}).map(function (p) {
return p.name
})
箭头函数
var f = function (a, b) { return a + b }
var f = (a, b) => { return a + b }
//函数体只有一句return的时候
var f = (a, b) => a + b
var f = (a) => a * a
//当函数参数只有一个
var f = a => a * a
ancestry.filter(p => p.died - p.born > 50).map(p => p.name)
f => (...args) => f(...args)
- 箭头函数没有arguments
function f(){
var g = () => {
console.log(arguments)
}
g()
}
f(1, 23, 4, 5)
//这里的arguments是普通变量 f函数传进来的 不是g的
var a = { "foo.bar" : 3 }
a.foo.bar
//错误 找a的foo属性 但a没有
a["foo.bar"]
-
序列化:对象 等等 内存里不是连续空间存储
用字符串表示分散的信息表达成连续的东西
自写forEach的break与continue
// forEach不能随时break 找到想要的值后也停不了 一定要全部遍历
function forEach(ary, action) {
for (var i = 0; i < ary.length; i++) {
var x = action(ary[i], i)
if (x === false) {
break
}
}
return ary //有返回值 不再是undefined 可以链式调用
}
var target = 3
var targetIndex = -1
forEach([1, 2, 3, 4], function (aryItem, idx) {
if (xx) {
return
}//相当于continue
if (aryItem == target) {
targetIndex = idx
return false
}//相当于break
}).map()
reduce
function reduce(ary, reducer, initial) {
let start = 0
if (arguments.length == 2) {
initial = ary[0]
start = 1
}//没有initial ary[0]做初始值
for (let i = start; i < ary.length; ++i) {
initial = reducer(initial, ary[i], i, ary)//这里initial位置要和后面对应
}
return initial
}
let result = reduce([1, 2, 3, 4], (previous, present) => previous + present)
console.log(result)
let all = ['foo', 'bar', 'zzz'].reduce((result, item) => {
result[item] = true
return result
}, {})
console.log(all)
// { foo: true, bar: true, zzz: true }
Composability
console.log(average(ancestry.filter(male).map(age)))
//ancestry.filter(male) 选出所有男性信息
//(......)map(age) 再选出他们的年龄
console.log(ancestry.filter((person => person.sex == 'm')).map(person => person.died - person.born))
the cost
效率会低一些
会产生临时数组 空间消耗
函数调用时间比普通循环慢 时间消耗
函数运行 调用栈 jump
keyBy
//假设祖先名字全不相同
var byName = {}
ancestry.foreEach((p) =>
byName[p.name] = p)
var byName2 = ancestry.reduce((obj, item) => {
obj[item.name] = item
return obj
}, {})
function keyBy(ary, key) {
let result = {}
// for (let i = 0; i < ary.length; ++i) {
// result[ary[i][key]] = ary[i]
// }
ary.forEach(item => {
result[item[key]] = item
})
return result
}
function keyBy(ary, key) {
return ary.reduce(function (obj, item) {
obj[item[key]] = item
return obj
}, {})
}
var byName = keyBy(ancestry, 'name')
var byName2 = (ary, key) => ary.reduce((obj, item) => ((obj[item[key]] = item), obj), {})
//逗号表达式返回右面的obj
groupBy
function groupby(ary, property) {
var result = {}
ary.forEach(item => {
var key = item[property]
if (!(key in result)) {
result[key] = []
}
result[key].push(item)
})
return result
}
var grouped = groupBy(ancestry, 'sex')
function groupBy(ary, f) {
var result = {}
ary.forEach(item => {
var key = f(item)
if (!(key in result)) {
result[key] = []
}
result[key].push(item)
})
return result
}
console.log(groupBy(ancestry, p => p.died - p.born))
function groupBy(ary, by) {
var f = by
if (typeof by == 'string') {
f = item => item[by]
}
var result = {}
ary.forEach(item => {
var key = f(item)
if (!(key in result)) {
result[key] = []
}
result[key].push(item)
})
return result
}