6 Higher-Order Function

231 阅读6分钟

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 even
    

    lexical 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
}