js的闭包及内存泄漏-回收机制
js的闭包,优缺点。
对于变量作用域的灵活使用;全局变量及局部变量。
js的特点:函数内部可以直接读取全局变量,但是在函数外部无法读取函数内部的局部变量。
闭包是为了完成,从外部读取函数内部的局部变量。
function f1(){
var n = 999;
function f2(){
console.log(n);
}
}
想要获取函数内部的局部变量的灵活处理方式就是,在函数内部再次定义一个函数。
函数外部无法读取函数内部的内容,函数内部却可以访问到全局变量,也因此要获取到函数内部的内容就是将定义一个函数将该函数变成该函数内部的定义函数。但是该内部函数的变量相对于父函数是不可见的。-js语言特有的链式作用域结构。(子对象会一级一级向上寻找所有父对象的变量。因此父对象的所有对象针对子对象都是可见的,但是子对象对于父对象是不可见的)
闭包的概念:f2函数就是一个闭包。闭包的作用就是能够读取其他函数的内部变量的函数。
js中只有函数内部的子函数才能读取局部变量。闭包就是一个函数内部的子函数。闭包是连接函数内部和函数外部的桥梁。
闭包的作用:1.读取函数内部的变量。2.让变量的值一直保持在内存当中,不会再f1调用之后被自动清除。f1是f2的父函数,而f2被赋给一个全局变量,导致f2始终存在于内存中,而f2的存在依赖于f1所以f1始终在内存中,不会调用结束之后,被垃圾回收机制回收,garbage collection回收。
nAdd= function(){n+=1} 首先nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数,而匿名函数本身也是一个闭包,所以,nAdd相当于一个setter,可以在函数外部对函数内部的局部变量进行操作。
使用闭包需要注意:1.由于闭包会使得函数中的变量被保存在内存中,对内存消耗很大,因此不能滥用闭包,否则会造成页面性能问题,在ie中可能导致内存泄漏问题。解决方法是,在退出函数之前,将不使用局部变量全部删除。2.闭包会在父函数外部,改变父函数内部变量的值。所以,如果把父函数作为对象,把闭包当做公用方法,把内部变量当做他的私有属性,这时一定要小心,不要随意改变父函数内部变量的值。
js的垃圾回收:
js中,js的执行环境会负责管理代码执行中的使用的内存,自动垃圾回收机制。
生命周期:当一个变量的生命周期结束之后,它所指向的内存就应该被释放。js的两种变量,全局变量和在函数中定义产生的局部变量。局部变量的生命周期在函数执行之后就结束了,此时就将其引用向内存释放。但是全局变量会持续到浏览器关闭页面。
js的垃圾回收方式:js执行环境中的垃圾回收器怎样才能检测哪块内存可以被回收,主要是两种方式,标记清除、引用计数。
标记清除:即函数执行时标记进入环境,函数执行完时,标记离开环境。在离开环境之后还有变量即标记方式不定,可以是某个特殊位的反转或维护一个列表。
垃圾收集器给内存中的所有变量都加上标记,然后去掉环境中的变量以及被环境中的变量引用的变量标记。在此之后,再被加上的标记的变量即为需要回收的变量,因为环境中的变量已经无法访问这些变量。
引用计数:这种方式可能会引起内存泄漏,在低版本的ie使用该方式。机制是跟踪一个值得引用次数,当声明一个变量并将一个引用类型赋值给该变量时该值引用次数加一,当这个变量指向其他一个时,该值得引用次数减一。当该值引用次数为0时,就会回收。
导致泄漏的原因是 不能解决循环引用的问题。(对象循环赋值,没有使用中间值)
function s(){var a={};var b={};a.prop = b; b.prop=a;}
这种情况下,导致每次调用该函数,a和b的计数引用都是2,会使这部分内存永远不会被释放,即导致内存泄漏(内存无法被释放或者使用,一直被占用。)