# 闭包和 IIFE

# 课程目标

  • 进一步学习 JavaScript 的函数性质,理解什么是一级函数以及函数作用域
  • 应用 map ,filter ,练习回调函数的使用
  • 掌握闭包和 IIFE 的应用场景并且学会实践闭包进行编码

# 函数是第一等公民

在 JavaScript 中,函数是第一等公民。这意味着,就像对象一样,你可以像处理其他元素(如数字、字符串、数组等)一样来处理函数。JavaScript 函数可以:

  • 存储在变量中。
  • 从一个函数返回。
  • 作为参数传递给另一个函数

既然我们知道函数是一级函数,我们可以将函数作为一个值十分简便地从函数返回函!返回另一个函数的函数被称为高阶函数

# 回调函数

还记得吗,JavaScript 函数是一级函数。我们可以像处理其他值一样来处理函数——包括将它们传递给其他函数!接受其他函数作为参数(和/或返回函数,如上一部分所述)的函数被称为高阶函数。作为参数传递给另一个函数的函数被称为回调函数。

# 任务一

基于 students 数组和 map(),编写匿名回调函数在 map()中调用,按如下要求处理 students 数据

  • 以下面的格式为数组中的每个项目返回一个字符串: <name> :学号 <ID> 总分<score>排名第<rank>
  • 将返回的数据存储在新的 studentsFullInfo 变量中
  • 不要删除 musicData 变量
  • 请勿更改任何 musicData 内容
let students = [
  { name: "Susan", ID: 1, score: "90", rank: 6 },
  { name: "Jackson", ID: 2, score: "88", rank: 7 },
  { name: "Bob", ID: 3, score: "45", rank: 18 },
  { name: "Jennie", ID: 3, score: "99", rank: 1 },
  { name: "Amy", ID: 3, score: "39", rank: 21 },
  { name: "Lisa", ID: 3, score: "78", rank: 8 },
];

let studentsFullInfo = "这里编写你的代码...";

console.log(studentsFullInfo);
// ["Susan:学号1总分90排名第6", "Jackson:学号2总分88排名第7"...]

# 任务二

基于 students 数组和 filter(),编写匿名回调函数在 filter()中调用,按如下要求处理 students 数据

  • 筛选出成绩不及格的学生,
  • 将返回的数据存储在新的 failList 变量中
let failList = "这里编写你的代码...";

console.log(failList);
// [{name : "Bob",ID:3,score:'45',rank:18}, {name : "Amy",ID:3,score:'39',rank:21},]

# 任务三

参考数组方法forEach (opens new window)方法功能,编码实现一个 foreach()函数

# 作用域&闭包

在理解闭包之前,我们需要理解 JavaScript 的作用域

闭包是指函数和该函数声明位置的词法环境的组合。每次定义函数时,都会为该函数创建闭包。对于在一个函数中定义另一个函数的情况,闭包尤其强大,它让嵌套函数可以访问其外部的变量。即使父函数已返回,函数也会保留一个到其父作用域的链接。这可以防止父函数内的数据被垃圾回收。

# 任务

编写一个闭包函数实现一个函数式编程,完成两个非递减数组按顺序合并功能函数,调用形式 -> merge(arr1)(arr2)。

给你两个按 非递减顺序 排列的整数数组  arr1 和 arr2,请你 合并 arr2 到 arr1 中,使合并后的数组同样按 非递减顺序 排列。

function merge(arr1) {
  // your code here
}

//测试用例
console.log(merge([1, 5, 8])([2, 4])); // =>[1,2,4,5,8]
console.log(merge([6, 9])([1, 2])); // =>[1,2,6,9]

# 任务二

# 立即调用函数表达式 (IIFE)

立即调用函数表达式 (IIFE) 是在定义之后立即被调用的函数。将 IIFE 和闭包结合使用可以创建一个私有作用域,从而维护内部定义变量的私有性。而且,由于所创建的变量较少,IIFE 将有助于最大限度地减少对全局环境的污染,从而降低变量名称冲突的几率。

# 任务

立即调用函数表达式 (IIFE)应用:请编码实现按钮点击次数监听

  • 用 count 变量记录按钮点击次数,
  • 按钮每点击一次,count 就加一
  • 当按钮点击了五次时,也就是 count = 5 时,count 清零重新计数,并弹出提示框您已点击五次按钮
  • 以上功能需要在按钮点击事件处理程序中完成
    • 要求 count 不能是全局变量,而是按钮点击事件局部私有变量
    • 使用立即调用函数表达式 (IIFE)和闭包创建一个私有作用域
var btn = document.querySelector("button");

btn.addEventListener(
  "click",
  (function() {
    // init the count to 0
    var count = 0;

    // your code here
  })()
);
<button>Click Me</button>

# 自测问题

  • 什么是一级函数?
  • 什么是回调函数
  • 什么是闭包?
  • 什么是立即调用函数表达式?
  • IIFE 有什么作用?
  • 下面这段代码在控制台显示什么?
let n = 2;

function myFunction() {
  let n = 8;
  console.log(n);
}

myFunction();