闭包
概念:是指有权访问另一个函数作用域中的变量的函数。
1.它的本质是什么
实际上就是一种函数,该函数描述了:函数访问变量时其作用域的问题。
怎么判断他是不是一个闭包呢?
闭包的表现形式(满足以下形式就是闭包):
使函数外部能够调用函数内部定义的变量。就形成了一个闭包。说复杂点就是当一个函数的返回值是另外一个函数,而返回的那个函数如果调用了其父函数内部的其它变量,如果返回的这个函数在外部被执行,就产生了闭包。
2.他产生的历史背景是什么?
2.1javaScript大环境下遵循以下规则:
函数内部可以读取函数外部的全局变量;
在函数外部无法读取函数内的局部变量。
2.2 javaScript的GC机制
在javaScript中,如果一个对象不再被引用,那么这个对象就会被GC回收,否则就会一直保存在内存中。
2.3为什么要使用闭包:
因为JS中变量的作用域分为全局变量和局部变量。在函数外部无法读取函数内的局部变量。需要闭包来解决。
2.4解决了什么问题:
描述:实现在函数外部可以读取函数内的局部变量,就出现了闭包。让函数执行完成后,内部的函数、变量还能被调用,可以采用闭包延长局部变量/函数的生命周期。
2.5使用场景:
排他、函数节流、网络…
3、闭包的作用:
1.使得在函数外部可以读取函数内的局部变量。
2.让这些变量的值始终保持在内存中
4、带来了什么问题:
滥用闭包,会造成内存泄漏;由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量指向null。
问题:想要访问内部函数的变量 name
Q:我想要在函数外部访问函数内部的变量
function test() {
var age = "18";
}
console.log(age)//无法访问
solve:
最简单的一个闭包形式
/* 仔细阅读这段文字其实就是 简单描述了以下代码的文字描述,有没有
当一个函数的返回值是另外一个函数,
而返回的那个函数如果调用了其父函数内部的其它变量,
如果返回的这个函数在外部被执行,就产生了闭包。
*/
function test() {
var age = 18; // 局部变量
return function () {
console.log(age);
}
}
var func = test();//将这个函数赋值给变量func
console.log(func);
/*
ƒ () {
console.log(age);
}
*/
func();//18 调用这个函数
/*
让函数执行完成后,内部的函数、变量还能被调用,
延长局部变量/函数的生命周期。达到在外界间接使用局部变量。
换句话说:使得在函数外部可以读取函数内的局部变量 */
闭包的使用场景:
1、 封闭作用域
(即使使用相同变量名称,都不会互相影响)
最大作用:全局变量私有化
优点:
不污染全局空间
内部所有的临时变量执行完毕都会释放不占内存。
可以保存全局数据
更新复杂变量
(funtion(){})();
;(funtion(){})();
+(funtion(){})();
-(funtion(){})();
?(funtion(){})();
(function () {
var age = 18;
console.log(age);//18
})();
(function () {
var age = 25;
console.log(age);//25
})();
2、作用域链
嵌套之间的函数会形成作用域链,每次对作用域的访问实际上都是对整条域链的遍历查找,先查找最近的作用域,然后再查找全局作用域,如果在某个作用域中找到了变量就停止查找。如下:
var num = 3;
(function () {
(function () {
(function () {
var num = 2;
(function () {
var age = 18;
console.log(num);//18
})();
})();
})();
})();
3、闭包解决性能优化问题
(function () {
var d = document;
//性能做了一个优化document就是找了一次
var btn = d.getElementById("btn");
var btn1 = d.getElementById("btn");
var btn2 = d.getElementById("btn");
var btn3 = d.getElementById("btn");
var btn4 = d.getElementById("btn");
})();
var num = 0;
for(var k in window){
num++;
//document的对象有很多,每次去查找耗费很多性能
document.write(num + "," + k + "<br/>")
}
//更加精简的做法 传参传到了全局就把document保存下来了
(function (document) {
var btn = document.getElementById("btn");
var btn1 = document.getElementById("btn");
var btn2 = document.getElementById("btn");
var btn3 = document.getElementById("btn");
var btn4 = document.getElementById("btn");
})(document);
4、闭包解决高级排他思想
//有20个li标签
var allLis = document.getElementsByTagName("li");
var lastOne = 0;//标记当前选中标签
for (var i = 0; i < allLis.length; i++) {
(function (index) {
allLis[index].onmouseover = function () {
// 清除 之前选中的current类
allLis[lastOne].className = "";
// 设置
this.className = "current";
// 赋值 将lastOne赋值给当前的index
lastOne = index;
}
})(i);
//i是外界传进来的,里面的index
//与外界的i保持同步
}
5、闭包的参数传递
要实现向左右走
function move(speed) {
var num = 0;
return function () {
num += speed;
this.style.marginLeft = num + 'px';
}
}
var lImg = document.getElementById("l");
var rImg = document.getElementById("r");
lImg.onmousedown = move(-50);
rImg.onmousedown = move(50);
6.、函数节流
比如在调用onresize()这个函数的时候,就会触发这个函数非常频繁,造成用户体验度不好。因此出现函数节流
var timer = null;
window.onresize = function () {
console.log(document.documentElement.clientWidth);
}
函数节流就是 加个定时器 让其触发变缓慢点
var timer = null;
window.onresize = function () {
clearTimeout(timer);
timer = setTimeout(function () {
console.log(document.documentElement.clientWidth);
}, 400);
}
用闭包改变后的节流函数
function throttle(fn, delay) {
var timer = null;
return function () {
clearTimeout(timer);
timer = setTimeout(fn, delay);
}
}
window.onresize = throttle(function () {
console.log(document.documentElement.clientWidth);
}, 400);
以下代码实例帮助理解:
<script type="text/javascript">
function a(){
var o=1;
function b(){
console.log(o++);
}
b();
}
console.log(a())//打印出来是1
console.log(a())//打印出来还是1
console.log(a())//打印出来也是1
//理由:因为每次调用a()中的变量都会释放,所以每次都是打印出来的是1
//问题来了:我们要怎样实现调用a()中的变量进行累加。实现变量不释放。
//解决方案:想办法让a函数调用一次不释放变量,b函数调用多次,就能实现o的累加;
//改变代码:闭包登场:
定义和用法:当一个函数的返回值是另外一个函数,而返回的那个函数如果调用了其父函数内部的其它变量,如果返回的这个函数在外部被执行,就产生了闭包。
function a(){
var o=1;
function b(){
console.log(o++);
}
return b;
}
var c=a();
console.log(c());//打印出1
console.log(c());//打印出2
console.log(c());//打印出3
//a()是返回的是b()这个函数;将a()这个函数赋值给c;类似把b()托 管给c;这样多次调用C的同时可以看成是多次调用了b()这个函数。
总结:(以上说了那么多废话只是帮助理解)
因为在函数外部无法读取函数内的局部变量。使用闭包使得在函 数外部可以读取函数内的局部变量。