高阶函数分析 | 青训营笔记

94 阅读2分钟

这是我参与「第四届青训营 」笔记创作活动的第2天。

在组队群里我们对月影老师课上的代码进行了分析、讨论。

代码如下:

function consumer(fn, time){
  let tasks = [],
      timer;
  
  return function(...args){
    tasks.push(fn.bind(this, ...args));
    if(timer == null){
      timer = setInterval(() => {
        tasks.shift().call(this)
        if(tasks.length <= 0){
          clearInterval(timer);
          timer = null;
        }
      }, time)
    }
  }
}

function add(ref, x){
  const v = ref.value + x;
  console.log(`${ref.value} + ${x} = ${v}`);
  ref.value = v;
  return ref;
}

let consumerAdd = consumer(add, 1000);

const ref = {value: 0};
for(let i = 0; i < 10; i++){
  consumerAdd(ref, i);
}

前置知识:

  1. 闭包

  2. 高阶函数

闭包(Closure)

我们先看一下在计算机科学中对闭包的定义(维基百科):

闭包(Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures);

是在支持 头等函数 的编程语言中,实现词法绑定的一种技术;

闭包跟函数最大的区别在于,当捕捉闭包的时候,它的自由变量会在捕捉时被确定,这样即使脱离了捕捉时的上下文,它也能照常运行;

MDN对JavaScript闭包的解释:

一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure);

大致总结就是: 一个函数如果访问到外层作用域的自由变量,这个函数就是一个闭包。

在代码中 function(...args) { ... } 这个函数就是闭包,访问到了外层作用域的fn, time, takes, timer 自由变量。

高阶函数

image.png

  1. 以函数为参数
  2. 以函数为返回值

两者满足一个就是高阶函数。

代码分析

consumer(add, 1000); 返回一个函数(闭包),并且将 add函数作为fn的值,1000作为time的值 绑定了起来,它赋值给了 consumerAdd函数

for (let i = 0; i < 10; i++) { consumerAdd(ref, i); }

for循环执行十次 consumerAdd函数 ,在第一次执行中,ref, i 传给了 args,接着 fn也就是add函数传入 args,并且push到了 task数组里

第一次执行的时候timer值为undefined,符合if条件,进入if语句里,然后遇到了setInterval异步操作,将里面的回调加入宏任务队列里,继续执行同步操作,即:循环完剩下的九次,push add函数到tasks数组

执行完同步操作,开始执行宏任务队列里面的回调,隔一个 time ,取出 task数组里面的 add函数 执行,当数组为空的时候,timer 重新设置为 null

可以去看一下打印的结果。

one more thing

tasks.push(fn.bind(this, ...args)); 这里的this值是什么? window