闭包

78 阅读1分钟

定义:

在一个函数内部的函数,可以使用其他函数的变量。
闭包是一种保护私有变量的机制,函数执行时形成私有作用域,保护私有变量不受外界影响。
外部函数调用之后其变量对象本应该被销毁,但闭包的存在使我们仍然可以访问外部函数的变量对象

// 闭包实现全局变量效果
var countFn = (function(){
    var count = 0;
    return function(){
        return count += 1;
    }
})()
countFn() // count = 1;
countFn() // count = 2;
countFn() // count = 3;

优缺点:

  • 优点:私有变量的存在、长期内存存储、避免全局污染
  • 缺点:内存浪费、常驻内存及其使用不当的无效内存增大内存使用量,导致内存泄漏
(function immediateA(a) {
  return (function immediateB(b) {
    console.log(a); // => ? a = 0
  })(1);
})(0);
// 0
let count = 0;
(function immediate() {
  if (count === 0) {
    let count = 1;
    console.log(count); // 输出什么?
  }
  console.log(count); // 输出什么?
})();
// 1 0
for (var i = 0; i < 3; i++) {
  setTimeout(function log() {
    console.log(i); // => ?
  }, 1000);
}
// 3 3 3
阶段1:for()重复3次,在每次循环都会创建一个新函数log(),该函数将捕获变量i,setTimeout()安排log()1000ms后执行,当for()循环完成时,变量i的值为3
阶段2:1000ms后,setTimeout()执行log()函数,log()读取变量i为3,并输出3
function createIncrement() {
  let count = 0;
  function increment() {
    count++;
  }
  let message = `Count is ${count}`;
  function log() {
    console.log(message);
  }
  return [increment, log];
} 
const [increment, log] = createIncrement();
increment(); 
increment(); 
increment(); 
log(); // => ?
// count is 0
increment()被调用3次,count为3
message变量存在于createIncrement函数的作用域内,其初始值为'count is 0',即使count变量增加了,message也始终为'count is 0'

function createIncrement() {
  let count = 0;
  function increment() {
    count++;
  }
  increment();//这里调用n次 message里的count会+n
  let message = `Count is ${count}`;
  function log() {
    console.log(message);
  }
  return [increment, log];
} 
const [increment, log] = createIncrement();
increment(); 
increment(); 
increment(); 
log(); // => count is 1

闭包的应用场景

函数节流

var timer = null;
window.onresize = function(){
    clearTimeout(timer);
    timer = setTimeout(function(){
        console.log(document.documentElement.clientWidth);
    },500)
}
// 使用闭包实现函数节流
function throttle(fn,delay){
    var timer = null;
    return function(){
        clearTimeout(timer);
        timer = setTimeout(fn,delay);
    }
}
window.onresize = throttle(function(){
    console.log(document.documentElement.clientWidth);
},400)

自执行函数

(function(){
    var name = 'zhangsan';
    var fn1 = function(){
        return name
    }
    fn2(fn1);
})()
function fn2(f){
    console.log(f());
}

循环赋值

for(var i=1;i<=10; i++){
    (function(j){
        setTimeout(function(){
            console.log(j)
        },j*1000)
    })(i)
}

函数防抖

function debounce(callback,time){
    var timer;
    return function(){
        if(timer){
            clearTimeout(timer);
        }
        timer = setTimeout(()=>{
            callback();
        },time)
    }
}
window.onresize = debounce(()=>{console.log(1)},500)