闭包
闭包指有权访问另一个函数作用域中变量的函数。
函数套函数,内部函数访问外部函数变量,必须return,从而避免了变量被销毁
//这个例子中,闭包就是fn()函数
function fn(){
var num=1;
function fun(){
console.log(num);
}
return fun;
}
有些人可能会问到,既然闭包这么麻烦,为什么不使用全局变量呢?是因为这种临时变量如果大量在全局中,会影响访问速度。如果有一个处理过程很耗时的函数,我们就要先将计算出的值存储起来,当调用这个函数时,会先在缓存中查找,找不到才会进行计算并更新缓存。闭包可以做到这一点,因为他不会释放外部的引用,从而函数内部的值得以保留。
闭包demo
点击小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(i);//不用自定义属性index,输出的都是4,因为内部访问不到for循环里的i,点击时,i已经变成了4
console.log(this.index);
}
}
闭包操作
//2.利用闭包的方式得到当前小li的索引号
for(var i=0;i<lis.length;i++){
//创建了4个立即执行函数
(function(i){//接收参数
lis[i].onclick=function(){
console.log(i);
}
// console.log(i);
})(i);//立即执行函数的参数,先把i读进去
}
这样子闭包会产生内存泄漏,数据都无法销毁
定时输出小li内容
错误示范:
var lis = document.querySelector('.nav').querySelectorAll('li')
for(var i=0;i<lis.length;i++){
setTimeout(function(){
console.log(lis[i].innerHTML);
},3000)
//计时器是异步的,在任务队列里,for是立即执行的,i早就被变成4了
}
使用闭包:
for(var i=0;i<lis.length;i++){
(function(i){
//使用闭包,i一直跟着变,最后在3s后输出所有
setTimeout(function(){
console.log(lis[i].innerHTML);
},3000)
})(i)
}
思考题
思考题1
本案例没有使用闭包,函数内部就没有局部变量
执行object.getNamefunc()–>
返回的function(){ return this.name }–>
返回this.name–>
object.getNamefunc()()相当于function()()即为立即执行函数–>
立即执行函数this指向的是window–>
this.name即为window.name=The Window
结果为The Window
思考题2
本案例使用了闭包,that使用了上一个函数的属性
中间将this指向的object存了起来
结果为My Object
闭包的常见使用场景
原生的setTimeout
// 使用setTimeout传递的第一个函数不能带参数
setTimeout(function(params) {
console.log(params)
}, 1000)
// undefined
函数的参数无法为将要执行的函数提供参数,此时就需要闭包
function log(num) {
return function() {
console.log(`第${num}秒后打印`)
}
}
setTimeout(log(2), 2000)
// 第2秒后打印
用闭包模拟私有方法
编程语言中JAVA
是支持将方法声明为私有的,而JavaScript
没有这种原生支持,但可以使用闭包来模拟私有方法。私有方法不仅仅有利于限制对代码的访问:还提供了管理全局命名空间的强大能力,避免了非核心的代码弄乱了代码的公共接口。
var Counter = (function() {
var privateCounter = 0 // 私有变量
function changBy(val) {
privateCounter += val
}
return {
intcrement: function() { // 三个闭包共用一个词法语境
changBy(1)
},
delcrement: function() {
changBy(-1)
},
value: function() {
return privateCounter
}
}
})()
console.log(Counter.value()) // 0
Counter.intcrement()
Counter.intcrement()
console.log(Counter.value()) // 2
Counter.delcrement()
console.log(Counter.value()) // 1
var Counter1 = Counter()
var Counter2 = Counter() // 每个闭包都是引用自己词法作用域内的变量privateCounter
console.log(Counter1.value(), Counter2.value()) // 0 0
Counter1.intcrement()
console.log(Counter1.value(), Counter2.value()) // 1 0
一个闭包内对变量的修改,不会影响另一个闭包的变量