说说你对闭包的理解
- 闭包是什么
- 闭包的三个特性
- 优缺点
- 应用场景
- javascript的垃圾回收原理:
- 通常情况下有两种实现方式
- 垃圾回收原理的缺陷
- GC优化策略
闭包是什么
- 闭包就是能够读取其他函数内部变量的函数
- 闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链域
闭包的三个特性
- 函数内再嵌套函数
- 内部函数可以引用外层的参数和变量
- 参数和变量不会被垃圾回收机制回收
优缺点
- 使用闭包主要是为了设计私有的方法和变量。
- 闭包的优点是可以避免全局变量的污染,让这些变量始终保持在内存中,可以读取函数内部的变量,还可以就是,能够实现封装和缓存等,私有成员的存在
- 缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。消耗内存、不正当使用会造成内存溢出的问题
- 在js中,函数即闭包,只有函数才会产生作用域的概念
应用场景
- 场景一:采用函数引用方式的setTimeout调用(和click一样)
原生的setTimeout传递的第一个函数不能带参数,通过闭包可以实现传参效果。
function f1(a) {function f2() {console.log(a);}return f2;
}
var fun = f1(1);
setTimeout(fun,1000);//一秒之后打印出1
- 场景二:沙箱模式可以将代码运行环境与外部环境隔离开来,防止互相干扰和污染。
// 创建一个沙箱函数,接收一个配置对象作为参数function createSandbox(config) {// 在沙箱函数内部定义私有变量和方法var privateData = config.data;function privateMethod() {// 私有方法的实现}// 返回一个包含公共接口的对象return {// 公共方法publicMethod: function () {// 使用私有数据和私有方法进行业务逻辑处理console.log(privateData);privateMethod();}};}// 使用沙箱函数创建一个沙箱实例var sandbox = createSandbox({data: "私有数据"});// 调用沙箱实例中的公共方法sandbox.publicMethod();
- 场景三
封装私有变量
如下面代码:用js创建一个计数器
function f1() {var sum = 0;var obj = {inc: function () {sum++;return sum;}};return obj;}let result = f1();console.log(result.inc(), '1');//1console.log(result.inc(), '2');//2console.log(result.inc(), '3');//3
function f1() {var sum = 0;function inc() {sum++;return sum}return inc;
}
let result = f1();
console.log(result(), '1');
console.log(result(), '2');
console.log(result(), '3');//3
function f1() {var sum = 0;function f2() {sum++;return f2;}//返回指定对象的原始值,若对象没有原始值,则将返回对象本身。f2.valueOf = function () {return sum;};f2.toString = function () {return sum + '';};return f2;
}
//执行函数f1,返回的是函数f2
console.log(+f1());//0
console.log(+f1()())//1
console.log(+f1()()())//2
javascript的垃圾回收原理:
Javascript具有自动垃圾回收机制(GC:Garbage Collecation)
- 在javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收:
- 如果两个对象互相引用,而不再被第三者所引用,那么这两个互相引用的对象也会被回收。
通常情况下有两种实现方式
标记清除
- js中最常用的垃圾回收方式就是标记清除。
当变量进入环境时,例如,在函数中声明一个变量,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为“离开环境”。
引用计数
- 跟踪记录每个值被引用的次数
当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1。当这个值的引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾回收器下次再运行时,它就会释放那些引用次数为0的值所占用的内存。
垃圾回收原理的缺陷
和其他语言一样,javascript的垃圾回收机制策略也无法避免一个问题:自动垃圾回收机制时,停止响应其他操作,这是为了安全考虑,而Javascript的GC在100ms甚至以上,对一般的应用还好,但对于JS游戏,动画对连贯性要求比较高的应用,就麻烦了。这就是新引擎需要优化的点:避免GC造成的长时间停止响应。
GC优化策略
- 分代回收(Generation GC)
这个和Java回收策略思想是一致的。目的是通过区分“临时”与“持久”对象;多回收“临时对象”区(young generation),少回收“持久对象”区(tenured generation),减少每次需遍历的对象,从而减少每次GC的耗时
- 增量GC
这个方案的思想很简单,就是每次处理一点,下次再处理一点,如此类推