垃圾回收
标记清除
当变量进入环境时,将其标记为“进入环境”。当变量离开环境时,则将其标记为“离开环境”。垃圾回收器会销毁那些带标记的值,并回收它们所占用的内存空间。
function test() {
var a = 1; // 函数调用时 被标记 进入上下文
}
test(); // 函数执行完毕,a的标记去掉,被回收
引用计数
当声明一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1。当这个值的引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。当垃圾回收器下次再运行时,它就会释放那些引用次数为0的值所占用的内存。
function test() {
var a = {}; // a的引用次数为0,被回收
var b = a; // a的引用次数加1,为1
var c = a; // a的引用次数再加1,为2
a = 1; // a的引用次数减1,为1
b = 1; // a的引用次数减1,为0,可以被回收了
c = 1; // a的引用次数减1,为0,可以被回收了
}
闭包
闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。闭包使得函数可以继续访问定义时的词法作用域。
闭包的另一个用处,是封装私有变量。
function createCounter() {
let count = 0;
return {
increment: function () {
count++;
},
getCount: function () {
return count;
},
};
}
const counter = createCounter();
counter.increment();
console.log(counter.getCount()); // 1
闭包的缺点:
- 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
- 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
- 闭包的缺点就是常驻内存,会增大内存使用量,并且使用不当很容易造成内存泄露。
闭包的用途
- 创建私有变量
function createCounter() { let count = 0; return { increment: function () { count++; }, getCount: function () { return count; }, }; }
- 模拟块级作用域
(function () { for (var i = 0; i < 10; i++) { console.log(i); } })();
- 实现柯里化
function add(a, b, c) { return a + b + c; } function curry(fn) { return function curried(...args) { if (args.length >= fn.length) { return fn.apply(this, args); } else { return function (...args2) { return curried.apply(this, args.concat(args2)); }; } }; } const curriedAdd = curry(add); console.log(curriedAdd(1)(2)(3)); // 6
- 实现函数节流和防抖
function throttle(fn, delay) { let timer = null; return function (...args) { if (!timer) { timer = setTimeout(() => { fn.apply(this, args); timer = null; }, delay); } }; } function debounce(fn, delay) { let timer = null; return function (...args) { clearTimeout(timer); timer = setTimeout(() => { fn.apply(this, args); }, delay); }; }
- 实现单例模式
function Singleton(fn) { let instance = null; return function (...args) { if (!instance) { instance = new (fn.bind(this, ...args))(); } return instance; }; } function User(name) { this.name = name; } const createUser = Singleton(User); const user1 = createUser('Alice'); const user2 = createUser('Bob'); console.log(user1 === user2); // true
- 实现模块化开发
const module = (function () { let privateVar = 0; function privateFunc() { console.log('privateFunc'); } return { publicFunc: function () { console.log('publicFunc'); }, }; })(); module.publicFunc(); // publicFunc module.privateFunc(); // TypeError: module.privateFunc is not a function