闭包是什么
- 闭包就是能够读取其他函数内部变量的函数
- 闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链域
闭包的三个特性
- 函数内再嵌套函数
- 内部函数可以引用外层的参数和变量
- 参数和变量不会被垃圾回收机制回收
优缺点
- 使用闭包主要是为了设计私有的方法和变量。
- 闭包的优点是可以避免全局变量的污染,让这些变量始终保持在内存中,可以读取函数内部的变量,还可以就是,能够实现封装和缓存等,私有成员的存在
- 缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。消耗内存、不正当使用会造成内存溢出的问题
- 在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');//1
console.log(result.inc(), '2');//2
console.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
这个方案的思想很简单,就是每次处理一点,下次再处理一点,如此类推