一、全局变量、局部变量
1. JS中变量按作用域可分为全局和局部变量,全局变量是作用于整个JS程序中的,局部变量可以理解为私有变量,即一个函数内部定义的变量,只供这个函数内部使用,对于其他函数或脚本代码是不可用的。全局和局部变量即便是名称相同,也是两个不同的变量,修改其中一个不会影响到另一个的值;
注意:在定义变量声明时需要使用到关键字var,但是也可以将其省略,不使用var关键字的变量无论是在函数内部还是外部都是一个全局变量。
二、JS闭包
1.JS闭包是指可访问上一层函数作用域里的变量的函数,即便上一层函数已经关闭;
其实可以这样理解JS的闭包就是指让此函数可以使用其他函数内部定义的局部变量;
例如:
function a(){
var i=10; //定义局部变量
function c(){ //嵌套函数
i++;
return i;
}
return c();
}
var fun=a(); //将函数赋给变量fun
console.log(fun); //console.log(); 控制台输出
这个例子是让控制台输出变量fun,即函数a(),a()函数返回函数c(),所以执行函数c(),这里就发生了JS闭包,即访问上一层函数作用域里的变量的函数,所以c()函数返回的i是11,即最终控制台输出11;
2.JS闭包的内存泄漏及占用不必要内存缺点
1) 内存溢出:指程序向系统申请的一定大小的内存,但是系统无法满足程序所需而造成的内存溢出;
内存泄漏:是指程序所申请的内存一直得不到释放,即未被GC回收;就是说在一般项目中,声明的变量被一直保存在内存中,但是这个变量使用完成后GC无法回收这个变量占用的内存给别的程序使用就称为内存泄漏;
/****************************************************************/
插入一点GC(garbage collection)概念:js垃圾回收机制
JavaScript引擎基础GC方案是:mark and sweep(标记清除),即:
遍历所有可访问的对象;
回收已不可访问的对象;
GC时,为安全考虑,停止响应其他操作。Js的GC在100ms甚至以上,对一般应用还好,但对于js游戏,动画对连贯性要求比较高的应用,则需要优化,避免GC造成长时间停止响应;
/****************************************************************/
2)由于闭包会使变量始终保持在内存中,这样若形成内存泄漏的堆积会导致消耗尽系统的内存,所以尽量避免不必要的闭包使用;
需注意的是:闭包并不会造成内存泄漏,内存泄漏是浏览器的bug;
例如:
function p1(){
var a=1;
b=function(){ //b不仅是一个全局变量,而且b的值是一个匿名函数本身是一个闭包
a+=1;
}
function p2(){
Console.log(a);
}
return p2;
}
var result=p1();
result(); //这里p1()返回的是p2,所以result()就相当于闭包p2()函数,p2()运行第一次
b();
result(); //p2()运行第二次
第一次运行结果是1 第二次经过b()函数加1,结果是2;证明了函数p1中的局部变量a一直保存在内存中,并没有在p1调用后被自动清除;p1是p2的父函数,而p2被赋给了一个全局变量,p2的存在依赖于p1,故p2始终存在于内存中,不会在调用结束后被GC回收;
3.闭包内存泄漏的解决办法:在退出函数之前,将不使用的局部变量全部删除;
4.在一个作用域定义2个闭包,这两个闭包会共享同样的私有变量:
例如:
function sum(){
var a=[];
for(var i=0;i<10;i++){
a[i]=function(){ //匿名函数 闭包 i为a[]角标
return i;
}
}
return a; //这里返回的a数组
}
var a=sum();
console.log(a[5]());
输出结果为10
代码中第一个陷阱是定义的变量a和函数内部数组a名称一致,需要注意,另外,闭包的匿名函数被嵌套在一个for循环里,相当于创建了10个闭包,并且这10个闭包被存储到一个数组中,又由于这些数组都是在同一个函数中被定义,所以共享变量i;所以sum()返回时,变量i的值是10,所有闭包都共享了这一值;
修改一下:
function sum(b){
return function(){
return b;
}
}
var a=[];
for(var i=0;i<10;i++){
a[i]=sum(i);
}
console.log(a[5]());
输出结果为5
5.闭包面试题举例
function fun(n,o) {
console.log(o)
return: { //返回对象
fun:function(m){
return fun(m,n); //返回最外层fun(n,o)
}
};
}
var a = fun(0); a.fun(1); a.fun(2); a.fun(3);
var b = fun(0).fun(1).fun(2).fun(3);
var c = fun(0).fun(1); c.fun(2); c.fun(3);
问:三行a,b,c的输出分别是什么?
第一行undefined,0,0,0
解析:a=fun(0) 传入实参n=0,o未定义,故输出的为undefined;
a.fun(1) 指fun(n,o)返回的对象fun(m),m=1,所以return fun(1,0) ,这时o=0,故输出的为0;
a.fun(2) 对象还是a 所以同理m=2,return fun(2,0)输出结果为0;
a.fun(3)同理,输出0;
第二行undefined,0,1,2
解析:第一个b=fun(0)同a=fun(0)输出undefined;
b=fun(0).fun(1)同a.fun(1)输出0
b=fun(0).fun(1).fun(2) 由上一步知return fun(1,0),所以fun(2)是指对象fun(m),m=2,这样return fun(2,1),所以输出为1
b = fun(0).fun(1).fun(2).fun(3)同理向下推输出为2
第三行 undefined,0,1,1
解析:c=fun(0).fun(1)同 b=fun(0).fun(1)输出undefined,0
c.fun(2)同b=fun(0).fun(1).fun(2)输出1
c.fun(3)这里对象还是c故输出还是1