概念
闭包:一个函数嵌套另一个函数,并且子函数可以访问外部的变量,就是闭包;
function fn() {
var a = 1;
return function fn1 () {
console.log(a)
}
}
var fn2 = fn(); // 返回fn1这个函数
fn2(); // a = 1
用途
计数器
function counter() {
var count = 0;
return function () {
return count++;
}
};
var fn = counter();
console.log(fn()) // 0
console.log(fn()) // 1
console.log(fn()) // 2
// 闭包使用完毕后,要进行释放,否则会造成内存泄漏
fn = null;
// 如果使用自调用,比如:fn()(),这样会导致每次调用都会被释放
// 就不会出现计数器
为什么是0,1,2,而不是1,1,1?
原因:fn 持有了 counter 函数的返回值,导致内部函数没有被释放,由于是闭包,并且 count 被引用,不满足垃圾回收机制,每次调用都使用的同一个 count ,因为存在引用,不会被垃圾回收掉,就造成每次调用的时候,保留了执行上下文中的变量,所以计数器会正常加1,而不是每次都是从0开始➕1
注意:一般使用完闭包后,必须要进行释放,否则就会引起内存泄漏,因为不释放的话,如果你反复调用 counter 函数创建多个闭包,就会造成内存累加,就会造成内存泄漏。
闭包可以封装对象的私有属性和方法
function Person() {
var age;
function setAge(n) {
age = n;
}
function getAge() {
return age;
}
return {
setAge,
getAge
}
}
var p1 = Person();
p1.setAge(18);
console.log(p1.getAge()) // 18
循环+闭包的用法
function fn() {
var arr = [];
for(var i = 0; i < 10; i++) {
arr[i] = function () {
return i;
}
}
return arr;
}
var fn1 = fn();
console.log(fn1[0]()) // 10
console.log(fn1[1]()) // 10
由于作用域和闭包的问题,当 fn1 已经创建完成的时候,内部函数使用的都是同一个 i ,并且在调用的时候 i 已经等于 10 了。
如果想要获取 0 1 2 3 这样的返回;
解决方案:
- 立即执行函数(IIFE)
function fn() {
var arr = [];
for(var i = 0; i < 10; i++) {
(function (n) {
arr[n] = function () {
return n;
}
})(i)
}
return arr;
}
var fn1 = fn();
console.log(fn1[0]()) // 0
console.log(fn1[1]()) // 1
通过自执行函数,这样每次循环都形成了一个独立的作用域,每次的 i 当作变量传入自执行函数中,内部函数会接收一个形参,这个形参的值就是档次循环的 i ;
- let 块级作用域
function fn() {
var arr = [];
for(let i = 0; i < 10; i++) {
arr[i] = function () {
return i;
}
}
return arr;
}
var fn1 = fn();
console.log(fn1[0]()) // 0
console.log(fn1[1]()) // 1
块级作用域,在for循环 + let 的时候,每次循环都是一个独立的块级作用域,并且把 i 值存储,所以执行的时候获取的都是当次执行的 i ;