function createGlobalVariables() { leaking1 = 'I leak into the global scope'; // 为未声明的变量赋值 this.leaking2 = 'I also leak into the global scope'; // 使用this指向全局对象 }; createGlobalVariables(); window.leaking1; window.leaking2;
function outer() { const potentiallyHugeArray = [];
return function inner() { potentiallyHugeArray.push('Hello'); // function inner is closed over the potentiallyHugeArray variable console.log('Hello'); }; }; const sayHello = outer(); // contains definition of the function inner
function repeat(fn, num) { for (let i = 0; i < num; i++){ fn(); } } repeat(sayHello, 10); // each sayHello call pushes another 'Hello' to the potentiallyHugeArray
function setCallback() { const data = { counter: 0, hugeString: new Array(100000).join('x') };
return function cb() { data.counter++; // data object is now part of the callback's scope console.log(data.counter); } }
setInterval(setCallback(), 1000); // how do we stop it?
那么应该如何避免上述这种情况的发生呢?可以从以下两个方法入手:
注意定时器回调引用的对象。
必要时取消定时器。
如下方的代码所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
function setCallback() { // 'unpacking' the data object let counter = 0; const hugeString = new Array(100000).join('x'); // gets removed when the setCallback returns
return function cb() { counter++; // only counter is part of the callback's scope console.log(counter); } }
const timerId = setInterval(setCallback(), 1000); // saving the interval ID
// doing something ...
clearInterval(timerId); // stopping the timer i.e. if button pressed
document.addEventListener('keyup', function() { // anonymous inline function - can't remove it doSomething(hugeString); // hugeString is now forever kept in the callback's scope });
那么如何避免这种情况呢?可以通过removeEventListener()释放监听器:
1 2 3 4 5 6
function listener() { doSomething(hugeString); }
document.addEventListener('keyup', listener); // named function can be referenced here... document.removeEventListener('keyup', listener); // ...and here
let user_1 = { name: "Peter", id: 12345 }; let user_2 = { name: "Mark", id: 54321 }; const mapCache = new Map();
function cache(obj){ if (!mapCache.has(obj)){ const value = `${obj.name} has an id of ${obj.id}`; mapCache.set(obj, value);
return [value, 'computed']; }
return [mapCache.get(obj), 'cached']; }
cache(user_1); // ['Peter has an id of 12345', 'computed'] cache(user_1); // ['Peter has an id of 12345', 'cached'] cache(user_2); // ['Mark has an id of 54321', 'computed']
console.log(mapCache); // ((…) => "Peter has an id of 12345", (…) => "Mark has an id of 54321") user_1 = null; // removing the inactive user
// Garbage Collector console.log(mapCache); // ((…) => "Peter has an id of 12345", (…) => "Mark has an id of 54321") // first entry is still in cache
let user_1 = { name: "Peter", id: 12345 }; let user_2 = { name: "Mark", id: 54321 }; const weakMapCache = new WeakMap();
function cache(obj){ // ...same as above, but with weakMapCache
return [weakMapCache.get(obj), 'cached']; }
cache(user_1); // ['Peter has an id of 12345', 'computed'] cache(user_2); // ['Mark has an id of 54321', 'computed'] console.log(weakMapCache); // ((…) => "Peter has an id of 12345", (…) => "Mark has an id of 54321"} user_1 = null; // removing the inactive user
// Garbage Collector
console.log(weakMapCache); // ((…) => "Mark has an id of 54321") - first entry gets garbage collected