前提条件
假设现有有个需求是写一个10*10的函数,那么很简单
function tenSqure() {
return 10 * 10
}
现在又有一个需求是写一个8*8的函数,也很简单
function tenSqure() {
return 8 * 8
}
这很无聊,所以很顺理成章地想到给函数一个参数,generialize这个函数
function multiply(num) {
return num * num
}
现在请记住: 因为传了一个参数,一个函数直到运行的时候才知道这个函数具体是干什么的
正题
现在有这样一段代码
function copyArrayAndMultiplyBy2(array) {
const output = []
for (let i = 0; i < array.length; i++) {
output.push(array[i] * 2)
}
return output
}
const myArray = [1, 2, 3]
const result = copyArrayAndMultiplyBy2(myArray)
逻辑很简单,就是一个数组每一项都乘以2然后返回
再看下面一段代码
function copyArrayAndDivideBy2(array) {
const output = []
for (let i = 0; i < array.length; i++) {
output.push(array[i] / 2)
}
return output
}
const myArray = [1, 2, 3]
const result = copyArrayAndDivideBy2(myArray)
是不是几乎一模一样,那么可以类似一开始的,传一个参数来generalize
function copyArrayAndManipulate(array, instructions) {
const output = []
for (let i = 0; i < array.length; i++) {
output.push(instructions(array[i]))
}
return output
}
function multiplyBy2(num) {
return num * 2
}
const result = copyArrayAndManipulate([1, 2, 3], multiplyBy2)
js在调用函数的时候可以简单理解为加了两句话
function copyArrayAndManipulate(array, instructions) {
const array = [1, 2, 3]
const instructions = multiplyBy2
// .....
}
这里的multiplyBy2
就是回调函数,这里的copyArrayAndManipulate
就是高阶函数(指的是要么参数传了函数,要么返回一个函数)
从以上的角度理解,高阶函数的意义和1010,88一样,是为了generalize函数,所以很自然地当做参数传了进去
美化一下
上面的代码等价代换一下就变成了 这样
function copyArrayAndManipulate(array, instructions) {
const output = []
for (let i = 0; i < array.length; i++) {
output.push(instructions(array[i]))
}
return output
}
/*
function multiplyBy2(num) {
return num * 2
}
*/
const result = copyArrayAndManipulate([1, 2, 3], num => num * 2)
形象记忆法
我一开始写回调总是不明白回调里函数的参数是哪来的,有了以上的理解就知道了参数不是你能决定的,当你在高阶函数里传一个回调的时候,回调的参数是看高阶函数里面提供了什么,意思就是高阶函数负责给你提供这个数据,具体你想用回调对这个数据干什么我不管。从output.push(instructions(array[i]))
这一句就可以看出来,array[i]
给你,instructions
干什么我不care。你是num⇒num*2
即multiplyBy2也好,num⇒num/2
即divideBy2也行。
所以这里提供了一个记忆方法:高阶函数负责提供食材,而回调负责用提供的食材做菜,具体做什么菜随意。
当需要自己写高阶函数的时候,只要想那个copyArrayAndMultiplyBy2
和copyArrayAndDivideBy2
的例子就好了。
当要调用别人提供的高阶函数的时候,就智能去看下API别人给你提供什么食材了。
练习
写一个Array的forEach
// 答案
Array.prototype.forEach = function(instructions){
for (let i = 0; i < this.length; ++i) {
instructions(this[i], i) // this[i]和i当做食材提供给instructions这个回调,instructions要做什么菜我不管
}
}
实际应用
基础应用
button.addEventListener('click', e ⇒ {
// e 就是浏览器给你的食材
})
扩展应用
有了这个食材和做菜的思想,就可以很容易地理解React的render props
和vue的scoped-slots
了(和高阶函数无关,只是思想类似)