5. 闭包
5.1 变量作用域
之前所学的知识:
5.2 什么是闭包
<script>
// 闭包(closure)指有权访问另一个函数作用域中变量的函数。
// 闭包: 我们fun 这个函数作用域 访问了另外一个函数 fn 里面的局部变量 num
//闭包就是一个函数,fn就是一个闭包,即被访问的局部变量num所在的函数fn就是一个闭包
//可以在scope中查看用到了闭包fn中的局部变量num
function fn() {
var num = 10;
function fun() {
console.log(num);
}
fun();
}
fn();
</script>
5.3 闭包的作用
闭包实现:在fn外部的作用域可以访问fn内部的局部变量
<script>
// 闭包(closure)指有权访问另一个函数作用域中变量的函数。
// 一个作用域可以访问另外一个函数的局部变量
// 我们fn 外面的作用域可以访问fn 内部的局部变量
// 闭包的主要作用: 延伸了变量的作用范围
function fn() {
var num = 10;
function fun() {
console.log(num);
}
return fun;
}
var f = fn();
// var f = fn();类似于
// var f = function() {
// console.log(num);
// }
f(); //当指向f()的时候会自动调用fun(),此时就会产生一个闭包,这个闭包指向fn
//虽然调用的是fun但是num是位于fn中的,所以这个闭包还是fn
</script>
<script>
// 闭包(closure)指有权访问另一个函数作用域中变量的函数。
// 一个作用域可以访问另外一个函数的局部变量
// 我们fn 外面的作用域可以访问fn 内部的局部变量
// 闭包的主要作用: 延伸了变量的作用范围
function fn() {
var num = 10;
//更简单的写法 直接返回函数 闭包也是一种高阶函数
return function() {
console.log(num);
}
}
var f = fn();
f(); //10
</script>
num等待所以函数执行完毕才会销毁。综上:闭包的主要作用就是沿伸了变量的作用范围。
5.5 闭包案例
1. 闭包应用-点击li输出当前li的索引号
<body>
<ul class="nav">
<li>榴莲</li>
<li>臭豆腐</li>
<li>鲱鱼罐头</li>
<li>大猪蹄子</li>
</ul>
<script>
// 闭包应用-点击li输出当前li的索引号
// 1. 我们可以利用动态添加属性的方式
var lis = document.querySelector('.nav').querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
lis[i].index = i;
lis[i].onclick = function() {
console.log(this.index);
}
}
// 2. 利用闭包的方式得到当前小li 的索引号
for (var i = 0; i < lis.length; i++) {
// 利用for循环创建了4个立即执行函数
// 立即执行函数也成为小闭包因为立即执行函数里面的任何一个函数都可以使用它的i这变量
(function(i) { //i:形参
lis[i].onclick = function() {
console.log(i);
}
})(i); //i:实参
}
//上述两种方式中闭包反而更麻烦,每次循环都要创建一个立即执行函数
//而且如果不进行点击事件,i就没办法销毁
</script>
</body>
端点放在console.log(i);上,点击某个li之后出现闭包,但是这个闭包没有名字,因为闭包是立即执行函数,点击事件的函数使用了立即执行函数里面的变量i,所以说立即执行函数就是一个闭包。
2. 闭包应用-3秒钟之后,打印所有li元素的内容
错误写法:
<body>
<ul class="nav">
<li>榴莲</li>
<li>臭豆腐</li>
<li>鲱鱼罐头</li>
<li>大猪蹄子</li>
</ul>
<script>
// 闭包应用-3秒钟之后,打印所有li元素的内容
var lis = document.querySelector('.nav').querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {//for循环是同步任务是立马执行
setTimeout(function() {
//错误写法
//这是一个异步任务是放到任务队列中,不是立即执行,这样写lis[i]会报错
// console.log(lis[i].innerHTML);
}, 3000)
}
</script>
</body>
异步任务主要有三种情况:定时器中的回调函数,事件中的回调函数,ajax中的回调函数(后面学)。异步任务只有被触发后才会执行
正确写法:
<script>
// 闭包应用-3秒钟之后,打印所有li元素的内容
var lis = document.querySelector('.nav').querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
(function(i) {
setTimeout(function() {
console.log(lis[i].innerHTML);
}, 3000)
})(i);
}
</script>
其实就是有4个立即执行函数,3秒之后,同时执行(打印)自己的内容
<script>
// 闭包应用-3秒钟之后,打印所有li元素的内容
var lis = document.querySelector('.nav').querySelectorAll('li');
(function(i) {
setTimeout(function() {
console.log(lis[i].innerHTML);
}, 3000)
})(0);
(function(i) {
setTimeout(function() {
console.log(lis[i].innerHTML);
}, 3000)
})(1);
(function(i) {
setTimeout(function() {
console.log(lis[i].innerHTML);
}, 3000)
})(2);
(function(i) {
setTimeout(function() {
console.log(lis[i].innerHTML);
}, 3000)
})(3);
</script>