开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第20天,点击查看活动详情
14、高阶函数
高阶函数(回调函数)
- 如果一个函数的参数或返回值是函数,则这个函数就称为高阶函数
- 为什么要将函数作为参数传递?(回调函数有什么作用?)
- 将函数作为参数,意味着可以对另一个函数动态的传递代码
<script>
class Person {
constructor(name,age){
this.name = name
this.age = age
}
}
const personArr = [
new Person('孙悟空',18),
new Person('沙和尚',37),
new Person('红孩儿',3),
new Person('白骨精',54),
]
function filter(arr,cb){
const newArr = []
for(let i = 0; i <arr.length; i++){
if(cb(arr[i])){
newArr.push(arr[i])
}
}
return newArr
}
// 我们这种定义回调函数的形式比较少见,通常回调函数都是匿名函数
function fn(a){
return a.name === '孙悟空'
}
result = filter(personArr,a => a.name === '孙悟空')
result = filter(personArr,a => a.age >= 18)
const arr = [1,2,3,,4,5,6,7,8,9,10]
result = filter(arr,a => a % 2 === 0)
console.log(result)
</script>
动态生成新数组,非破坏性函数
希望在someFn()函数执行时,可以记录一条日志
在不修改原函数的基础上,为其增加记录日志的功能
可以通过高阶函数,来动态的生成一个新函数
<script>
function someFn(){
return 'hello'
}
function outer(cb){
return () => {
console.log('记录日志~~~')
const result = cb()
return result
}
}
let result = outer(someFn)
function test(){
console.log("test~~~")
return 'test'
}
let newTest = outer(test)
newTest()
</script>
15、函数
创建一个函数,第一次调用时打印1,第二次调用打印2,以此类推
可以利用函数,来隐藏不希望被外部访问到的变量
闭包:
闭包就是能访问到外部函数作用域中变量的函数
什么时候使用:
当我们需要隐藏一些不希望被别人访问的内容时就可以使用闭包
构成闭包的要件:
- 函数的嵌套
- 内部函数要引用外部函数中的变量
- 内部函数要作为返回值返回
<script>
// let num = 0
// function fn(){
// num++
// console.log(num)
// }
// fn()
function outer(){
let num = 0 // 位于函数作用域中
return () => {
num++
console.log(num)
}
}
const newFn = outer()
// console.log(newFn)
newFn()
newFn()
newFn()
</script>
函数在作用域,在函数创建时就已经确定的(词法作用域)
和调用的位置无关
闭包利用的就是词法作用域
<script>
let a = '全局变量a'
function fn(){
console.log(a)
}
function fn2(){
let a = 'fn2中的a'
fn()
}
fn2()//因为后来调用了方法fn(),该方法是定义在全局中的,所以调用的是'全局变量a'
function fn3(){
let a = 'fn3中的a'
function fn4(){
console.log(a)
}
return fn4;
}
let fn4 = fn3()
fn4()
</script>
16、闭包
闭包的生命周期:
- 闭包在外部函数调用时产生,外部函数每次调用都会产生一个全新的闭包
- 在内部函数丢失时销毁(内部函数被垃圾回收了,闭包才会消失)
注意事项:
闭包主要用来隐藏一些不希望被外部访问的内容,
这就意味着闭包需要占用一定的内存空间
注意事项:
相较于类来说,闭包比较浪费内存空间(类可以使用原型而闭包不能),
需要执行次数较少时,使用闭包
需要大量创建实例时,使用类
<script>
function outer(){
let someVariable = 'someValue'
return function(){
console.log(someVariable)
}
}
function outer2(){
let num = 0;
return () => {
num++
console.log(num)
}
}
let fn1 = outer2()//独立闭包
let fn2 = outer2()
fn1()
fn2()
fn1 = null
fn2 = null
</script>
17、递归
递归
- 调用自身的函数称为递归函数
- 递归的作用和循环是基本一直
递归的核心思想
将一个大的问题拆分为一个一个小的问题,小的问题解决了,大的问题也就解决了
两个要件:
1.基线条件 —— 递归的终止条件
2.递归条件 —— 如何对问题进行拆分
递归的作用和循环是一致的,不同点在于,递归思路的比较清晰简洁,循环的执行性能比较好
在开发中,一般的问题都可以通过循环解决,也是尽量去使用循环,少用递归
只在一些使用循环解决比较麻烦的场景下,才使用递归
如:求斐波那契数列中的第n个数
<script>
/*
一对兔子出生后的两个月后每个月都能生一对小兔子
- 编写一个函数,可以用来计算第n个月的兔子的数量
1 2 3 4 5 6 7 8 9 10 11 12
1 1 2 3 5 8 13 21 34 ....
- 规律,当前数等于前两个数之和(斐波那契数列)
*/
function fib(n) {
// 确定基线条件
if (n < 3) {
return 1
}
// 设置递归条件
// 第n个数 = 第n-1个数 + 第n-2个数
return fib(n - 1) + fib(n - 2)
}
let result = fib(10)
console.log(result)
</script>