- 闭包是由函数以及声明该函数的词法环境组合而成的。该环境包含了这个闭包创建时作用域内的任何局部变量。
定义:
函数套函数,内部函数使用了外部函数定义的局部变量。
用处:
1.读取函数内部的变量;
2.这些变量的值始终保持在内存中,不会在外层函数调用后被自动清除。
作用:
1.希望一个变量长期驻扎在内存当中,不会被内存回收机制回收,即延长变量的生命周期;
2.避免全局变量的污染;
3.私有成员的存在。
特性:
1.函数套函数;
2.内部函数可以直接使用外部函数的局部变量或参数;
3.变量或参数不会被垃圾回收机制回收。
缺点:
常驻内存 会增大内存的使用量 使用不当会造成内存泄露,详解:
(1)由于闭包会将它的外部函数的作用域也保存在内存中
,因此会比其他函数更占用内存,这样的话,如果过度使用闭包,就会有内存泄露的威胁。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
内存泄漏
:程序的运行需要内存。对于持续运行的服务进程,必须及时释放不再用到的内存,否则占用越来越高,轻则影响系统性能,重则导致进程崩溃。不再用到的内存,没有及时释放,就叫做内存泄漏。
基本类型变量(Number 、Boolean、Undefined、String、Null)的值一般都是存在栈内存中,
引用类型变量(Array、Object、Function)的值存储在堆内存中,栈内存存储对应空间地址
(2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
<script>
function add() {
var a = 1
function fn() {
return a++
}
return fn
}
var c = add()
console.log(c()) //1
console.log(c()) //2
console.log(c()) //3
a = 100
console.log(c()) //4
/*
//js垃圾回收机制影响,此法a的值不会变,因为没有全局变量调用
console.log(add()()) //1
console.log(add()()) //1
console.log(add()())//1
console.log(add()())//1
*/
</script>
问题:
1 闭包有哪些应用场景?
(1)柯里化函数
为了避免频繁地调用具有相同参数的函数,可以将一个多参数的函数转化为一个单参数的函数,其实就是一个高阶函数
//普通函数
function getArea(w,h){
return w * h;
}
const area1 = getArea(10,20);
const area2 = getArea(10,30);
const area3 = getArea(10,40);
//柯里化函数
function getArea(w){
return function(h){
return w * h;
}
}
const getTenArea = getArea(10);
const area1 = getTenArea(20);
const area2 = getTenArea(30);
const area3 = getTenArea(40);
(2)通过闭包实现变量/方法的私有化
function funOne(i){
function getTwo(){
console.log('参数:', i)
}
return getTwo;
}
const fa = funOne(100);
const fb = funOne(200);
const fc = funOne(300);
(3)匿名自执行函数
var funOne = (function(){
var num = 0;
return function(){
num++;
return num;
}
})()
console.log(funOne()); // 1
console.log(funOne()); // 2
console.log(funOne()); // 3
(4)缓存一些结果
比如:外部函数定义一个变量,内部函数可以获取或修改这个变量的值,从而就延长了这个变量的生命周期
function parent(){
let list = [];
function son(i){
list.push(i);
}
return son;
}
const fn = parent();
fn(1);
fn(2);
fn(3);
2 闭包的this指向
闭包的this指向的是window
3 如何避免闭包引起的内存泄漏?
(1)在退出函数之前,将不使用的局部变量赋值为null;(示例如下)
这段代码会导致内存泄露
window.onload = function(){
var el = document.getElementById("id");
el.onclick = function(){
alert(el.id);
}
}
解决方法为
window.onload = function(){
var el = document.getElementById("id");
var id = el.id; //解除循环引用
el.onclick = function(){
alert(id);
}
el = null; // 将闭包引用的外部函数中活动对象清除
}
(2)避免变量的循环赋值和引用。 (示例如上)
(3)利用Jquery释放自身指定的所有事件处理程序。
由于jQuery考虑到了内存泄漏的潜在危害,所以它会手动释放自己指定的所有事件处理程序。 只要坚持使用jQuery的事件绑定方法,就可以一定程度上避免这种特定的常见原因导致的内存泄漏。
这段代码会导致内存泄露
$(document).ready(function() {
var button = document.getElementById('button-1');
button.onclick = function() {
console.log('hello');
return false;
};
});
当指定单击事件处理程序时,就创建了一个在其封闭的环境中包含button变量的闭包。而且,现在的button也包含一个指向闭包(onclick属性自身)的引用。这样,就导致了在IE中即使离开当前页面也不会释放这个循环。
用jQuery化解引用循环
$(document).ready(function() {
var $button = $('#button-1');
$button.click(function(event) {
event.preventDefault();
console.log('hello');
});
});