# 闭包和 IIFE
# 课程目标
- 进一步学习 JavaScript 的函数性质,理解什么是一级函数以及函数作用域
- 应用 map ,filter ,练习回调函数的使用
- 掌握闭包和 IIFE 的应用场景并且学会实践闭包进行编码
# 函数是第一等公民
在 JavaScript 中,函数是第一等公民。这意味着,就像对象一样,你可以像处理其他元素(如数字、字符串、数组等)一样来处理函数。JavaScript 函数可以:
- 存储在变量中。
- 从一个函数返回。
- 作为参数传递给另一个函数
既然我们知道函数是一级函数,我们可以将函数作为一个值十分简便地从函数返回函!返回另一个函数的函数被称为高阶函数
- First-class Function(头等函数) (opens new window)
- JavaScript 深入浅出第 2 课:函数是一等公民是什么意思呢? (opens new window)
# 回调函数
还记得吗,JavaScript 函数是一级函数。我们可以像处理其他值一样来处理函数——包括将它们传递给其他函数!接受其他函数作为参数(和/或返回函数,如上一部分所述)的函数被称为高阶函数。作为参数传递给另一个函数的函数被称为回调函数。
- JavaScript 回调函数 (opens new window)
- 简介:回调 | JavaScript 现代教程 (opens new window)
- 详解 JavaScript 回调函数 (opens new window)
- Array.prototype.map() (opens new window)
- Array.prototype.filter() - JavaScript | MDN (opens new window)
- Array.prototype.forEach() - JavaScript | MDN (opens new window)
# 任务一
基于 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 的作用域
- 变量作用域 (opens new window)
- 你想知道的关于 JavaScript 作用域的一切 (opens new window)
- JavaScript 中 let 和 const 的作用域 (opens new window)
- 理解 Javascript 的作用域和作用域链 (opens new window)
- 内存管理 (opens new window)
- ES5 规范中的词法环境(英) (opens new window)
闭包是指函数和该函数声明位置的词法环境的组合。每次定义函数时,都会为该函数创建闭包。对于在一个函数中定义另一个函数的情况,闭包尤其强大,它让嵌套函数可以访问其外部的变量。即使父函数已返回,函数也会保留一个到其父作用域的链接。这可以防止父函数内的数据被垃圾回收。
- 闭包 | MDN (opens new window)
- JavaScript 闭包教程 - 带有 JS 闭包示例代码 (opens new window)
- JavaScript 闭包是如何工作的? (opens new window)
# 任务
编写一个闭包函数实现一个函数式编程,完成两个非递减数组按顺序合并功能函数,调用形式 -> 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 简介 - 立即调用的函数表达式 (opens new window)
- IIFE(立即调用函数表达式) | MDN (opens new window)
- JavaScript Immediately-invoked Function Expressions (IIFE) (opens new window)
- JavaScript 中的 IIFE 是什么? (opens new window)
# 任务
立即调用函数表达式 (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();