在讲解闭包之前,我们先来了解一下闭包是用来干嘛的,和闭包的由来:
由来:
我们都知道,js的作用域分两种,全局和局部,基于我们所熟悉的作用域链相关知识,我们知道在js作用域环境中访问变量的权利是由内向外的,内部作用域可以获得当前作用域下的变量并且可以获得当前包含当前作用域的外层作用域下的变量,反之则不能,也就是说在外层作用域下无法获取内层作用域下的变量,同样在不同的函数作用域中也是不能相互访问彼此变量的,而我们想在一个函数内部也有限权访问另一个函数内部的变量,这就产生了闭包
作用:
可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后
接下来我们就来看看闭包是什么?
一、闭包的概念
闭包函数:声明在一个函数中的函数叫做闭包函数。
闭包:内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后。
闭包的特点:
- 让外部访问函数内部变量成为可能;
- 局部变量会常驻在内存中;
- 可以避免使用全局变量,防止全局变量污染;
- 会造成内存泄漏(有一块内存空间被长期占用不被释放,参数和变量不参与垃圾回收机制)
二、闭包的创建
闭包就是可以创建一个独立的环境,每个闭包里面的环境都是独立的,互不干扰。闭包会发生内存泄漏,每次外部函数执行的时 候,外部函数的引用地址不同,都会重新创建一个新的地址。但凡是当前活动对象中有被内部子集引用的数据,那么这个时候,这个数据不删除,保留一根指针给内部活动对象。
这样接受比较难理解,接下来我们从例子中来理解:
例子1
function funA(){
var a = 10; // 外部函数的参数;
return function(){ //闭包函数;
alert(a);
}
}
var b = funA();
b();
这就是一个简单的闭包,可以访问其所在的外部函数中声明的参数和变量
例子2
function outerFn(){
var i = 0;
function innerFn(){
i++;
console.log(i);
}
return innerFn;
}
//每次外部函数执行的时候,都会开辟一块内存空间,外部函数的地址不同,都会重新创建一个新的地址
//开辟新空间1
var inner = outerFn();
inner();
inner();
inner();
//输出 1 2 3
//开辟新空间2
var inner2 = outerFn();
inner2();
inner2();
inner2();
//输出 1 2 3
每次外部函数执行的时候,外部函数的引用地址不同,都会重新创建一个新的地址
例子3
var i = 0;
function outerFn(){
var j=0;
function innnerFn(){
i++;
j++;
console.log(i,j);
}
return innnerFn;
}
var inner1 = outerFn();
var inner2 = outerFn();
inner1();//1 1
inner2();//2 1
inner1();//3 2
inner2();//4 2
虽然外部函数的引用地址不同,会重新创建一个新的地址,但是引用了全局变量,全局变量的引用没有改变
例子4
function fn(){
var a = 3;
return function(){
return ++a;
}
}
alert(fn()()); //4
alert(fn()()); //4
这是常见的返回闭包函数,每次引用都赋予了新的地址
例子5
function outerFn(){
var i = 0;
function innnerFn(){
i++;
console.log(i);
}
return innnerFn;
}
var inner1 = outerFn();
var inner2 = outerFn();
inner1();//1
inner2();//1
inner1();//2
inner2();//2
外部函数被返回(寿命终结)了之后,可以访问其所在的外部函数中声明的参数和变量
例子6
(function() {
var m = 0;
function getM() { return m; }
function seta(val) { m = val; }
window.g = getM;
window.f = seta;
})();
f(100);
console.info(g()); //100
闭包找到的是同一地址中父级函数中对应变量最终的值,前提是已经得出了最终变量和值
建议大家自行运行一遍代码,这样理解更加深刻