引子
闭包,是 javascript 中很重要的一个概念,几乎每次面试都会被问及,然而却是晦涩难懂,令面试者无所适从。本文尽可能用简单易懂的话,讲清楚闭包的概念及其常见的面试题。
我们先来看一个例子:
function outer(){
var a = 888;
function inner(){
console.log(a);
}
}
inner();//报错
inner这个函数不能在outer外面调用,因为outer外面没有inner的定义,然而如果我非要让inner函数在作用域外执行,该怎么修改上述代码?
本文框架图
闭包是什么
我们可以对上面代码进行如下修改:
function outer(){
var a = 888;
function inner(){
console.log(a);
}
return inner; //outer返回了inner的引用
}
var inn = outer(); //inn就是inner函数了
inn(); //执行inn,全局作用域下没有a的定义,
//但是函数闭包,能够把定义函数的时候的作用域一起记住,输出888
在outer()执行之后,其返回值也就是内部的inner函数赋值给变量inn并调用了inn(),实际上就是通过不同的标识符引用调用了内部函数inner。调用inn()的时候,注意这个调用地点,我们是在outer函数外部调用的inn(),也就是说,在调用的这个地方,根本就不存在a变量的作用域。
但是神奇的是,控制台居然输出了数字1。这就说明了,inner函数能够持久保存自己定义时的所处环境,并且即使自己在其他的环境被调用的时候,依然可以访问自己定义时所处环境的值。
那到底什么是闭包呢?
当函数可以记住并访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这就产生了闭包。 ----《你不知道的Javascript上卷》
我个人理解,闭包就是函数中的函数(其他语言不能函数再套函数),里面的函数可以访问外面函数的变量,外面的变量的是这个内部函数的一部分。
闭包的特性
每个函数都是闭包,每个函数天生都能够记忆自己定义时所处的作用域环境。把一个函数从它定义的那个作用域,挪走,运行。这个函数居然能够记忆住定义时的那个作用域。不管函数走到哪里,定义时的作用域就带到了哪里。接下来我们用两个例子来说明这个问题:
//例题1
var inner;
function outer(){
var a=250;
inner=function(){
alert(a);//这个函数虽然在外面执行,但能够记忆住定义时的那个作用域,a是250
}
}
outer();
var a=300;
inner();//一个函数在执行的时候,找闭包里面的变量,不会理会当前作用域。
//例题2
function outer(x){
function inner(y){
console.log(x+y);
}
return inner;
}
var inn=outer(3);//数字3传入outer函数后,inner函数中x便会记住这个值
inn(5);//当inner函数再传入5的时候,只会对y赋值,所以最后弹出8
闭包的内存泄漏
栈内存提供一个执行环境,即作用域,包括全局作用域和私有作用域,那他们什么时候释放内存的?
- 全局作用域----只有当页面关闭的时候全局作用域才会销毁
-
私有的作用域(只有函数执行会产生私有的作用域)
一般情况下,函数执行会形成一个新的私有的作用域,当私有作用域中的代码执行完成后,我们当前作用域都会主动的进行释放和销毁。但当遇到函数执行返回了一个引用数据类型的值,并且在函数的外面被一个其他的东西给接收了,这种情况下一般形成的私有作用域都不会销毁。如下面这种情况:
function fn(){
var num=100;
return function(){
}
}
var f=fn();//fn执行形成的这个私有的作用域就不能再销毁了
也就是像上面这段代码,fn函数内部的私有作用域会被一直占用的,发生了内存泄漏。所谓内存泄漏指任何对象在您不再拥有或需要它之后仍然存在。闭包不能滥用,否则会导致内存泄露,影响网页的性能。闭包使用完了后,要立即释放资源,将引用变量指向null。接下来我们看下有关于内存泄漏的一道经典面试题:
<script>
function outer(){
var num=0;//内部变量
return function add(){//通过return返回add函数,就可以在outer函数外访问了。
num++;//内部函数有引用,作为add函数的一部分了
console.log(num);
};
}
var func1=outer();//
func1();//实际上是调用add函数, 输出1
func1();//输出2 因为outer函数内部的私有作用域会一直被占用
var func2=outer();
func2();// 输出1 每次重新引用函数的时候,闭包是全新的。
func2();// 输出2
</script>
闭包的作用
1.使用闭包可以访问函数中的变量。
2.可以使变量长期保存在内存中,生命周期比较长。
3.闭包的函数另外的意义:可以用来实现模块化。请看下面的例子:
var common=(function(){
var name="通用模块";
function initPage(){
alert(name);
return {
initPage2:initPage
})();
common.initPage2();//通用模块
网友评论