闭包
闭包是一种函数,它是一个可以访问其他函数内部变量的函数,闭包一般以函数作为返回值
特点
1.函数嵌套函数 内部函数被reture
2.让外部访问函数内部变量成为可能;
3.参数和变量不会被垃圾回收机制回收 可以让局部变量常驻在内存中;
4.延伸了变量的作用范围
5.可以避免使用全局变量,防止全局变量污染;
6.会造成内存泄漏(有一块内存空间被长期占用,而不被释放)
使用:
1.读取函数内部的变量
2.这些变量的值始终保持在内存中 不会在外层函数调用后被自动清除
优点:
1.变量长期驻扎在内存中
2.避免全局变量的污染
3.私有成员的存在
缺点:
会造成内存泄漏
JS中常见的内存泄漏
1.意外的全局变量
2.未被清空的计时器或回调函数
3.脱离DOM的引用
4.闭包
5.未被销毁的事件监听
什么是内存泄露
内存泄漏是指一块被分配的内存既不能使用又不能回收,直到浏览器进程结束
释放内存的方法:赋值为null
为什么会导致内存泄露
内存泄漏指我们无法在通过js访问某个对象,而垃圾回收机制却认为该对象还在被使用,因此垃圾回收机制不会释放该对象,导致该内存永远无法释放,积少成多,系统会越来越卡
垃圾回收机制都有哪些策略
标记清除法
垃圾回收机制获取根并标记他们,然后访问并标记所有来自它们的引用,然后在访问这些对象并标记它们的引用…如此递进结束后若发现有没有标记的(不可达的)进行删除,进入执行环境的不能进行删除
引用计数法
当声明一个变量并给该变量赋值一个引用类型的值时候,该值的计数+1,当该值赋值给另一个变量的时候,该计数+1,当该值被其他值取代的时候,该计数-1,当计数变为0的时候,说明无法访问该值了,垃圾回收机制清除该对象
缺点: 当两个对象循环引用的时候,引用计数无计可施。如果循环引用多次执行的话,会造成崩溃等问题。所以后来被标记清除法取代。
经典闭包例子
function car(){
var speed = 0
function fn() {
speed++
console.log(speed);
}
return fn
}
var speedUp = car()
speedUp() //1
speedUp() //2
闭包的应用
1.利用闭包的方式得到当前小li 的索引号
// 利用闭包的方式得到当前小li 的索引号
var lis = document.querySelector('.nav').querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
// 利用for循环创建了4个立即执行函数
// 立即执行函数也成为小闭包因为立即执行函数里面的任何一个函数都可以使用它的i这变量
(function(i) { //i为接受的参数
lis[i].onclick = function() {
console.log(i); //function使用了立即执行函数的变量
}
})(i); //i为传递的参数
}
2.计算打车价格
// 打车起步价13(3公里内), 之后每多一公里增加 5块钱. 用户输入公里数就可以计算打车价格
// 如果有拥堵情况,总价格多收取10块钱拥堵费
// function fn() {};
// fn();
var car = (function() {
var start = 13; // 起步价 局部变量
var total = 0; // 总价 局部变量
return {
// 正常的总价
price: function(n) {
if (n <= 3) {
total = start;
} else {
total = start + (n - 3) * 5
}
return total;
},
// 拥堵之后的费用
yd: function(flag) {
return flag ? total + 10 : total;
}
}
})();
console.log(car.price(5)); // 23
console.log(car.yd(true)); // 33
console.log(car.price(1)); // 13
console.log(car.yd(false)); // 13
3.埋点(是网站分析的一种常用的数据采集方法)计数器
<button>1</button>
<button>2</button>
<script>
function count() {
var num = 0;
return function () {
return ++num
}
}
var getNum = count(); // 第一个需要统计的地方
var getNewNum = count(); //第二个需要统计的地方
// 如果我们统计的是两个button的点击次数
document.querySelectorAll('button')[0].onclick = function () {
console.log('点击按钮1的次数:' + getNum());
}
document.querySelectorAll('button')[1].onclick = function () {
console.log('点击按钮2的次数:' + getNewNum());
}
一句话总结:闭包就是一个可以访问其他函数内部变量的函数
补充:
闭包产生的原因
函数中的局部变量在函数执行完后会被销毁,有时候,我们不希望这个局部变量会被销毁,我们还想在外部进行持续的操作和访问,我们就会用到闭包这种方式。
为什么不创建一个全局变量来代替这个局部变量?
因为全局变量会被污染或者被修改。
闭包能够访问里面的变量,是由于作用域链,用到了函数嵌套中,内部函数能够访问父级函数作用域的变量这个理念。
闭包能够造成内存泄漏
页面不关闭,变量就一直在,不能被垃圾回收机制回收或者手动清除。