JavaScript闭包详解及其应用场景

108 阅读2分钟

闭包

在javascript中变量的存在是有生命周期的,函数里面的变量当函数执行完成后就会被销毁,但是通过闭包可以延长变量的生命周期。 闭包是在函数内部使用了外部作用域中的变量,使得该变量无法被正常销毁。闭包形成了一个封闭的持续存在的作用域,具有封闭和持续的特性,闭包的功能非常强大,例如单例模式、防抖、节流、柯理化、模块化等。

最简单的闭包代码

function outer() {
    let count = 0;
    return function inner() {
        console.log(++count);
    }
}

const func = outer();
func(); // 1
func(); // 2

闭包的应用场景

  • 单例模式(Singleton)

单例模式应用于全局只需要一个实例的场景,例如全局的loading,全局的弹窗等,具有节省代码、方便统一管理的优点。

function Singleton() {
    let instance;
    function createInstance(name) {
        this.name = name;
        this.sayName = function() {
            console.log(this.name);
        }
    }
    return function(name){
        if(!instance){
            return instance = new createInstance(name);
        }else{
            return instance;
        }
    }
}

const createSingleton = Singleton();
const obj1 = createSingleton('obj1');
const obj2 = createSingleton('obj2');

obj1.sayName(); // obj1
obj2.sayName(); // obj1
  • 防抖函数(Debounce)

防抖函数是指事件在n秒内只能触发一次,n秒内再次触发事件会重新计时,常用于减少表单提交次数、减少搜索框实时搜索调用接口的次数。

function debounce(func, dealy) {
  let timerId;
  return function () {
    if (timerId) {
      clearTimeout(timerId);
      timerId = null;
    } else {
      timerId = setTimeout(() => {
        func.apply(this, arguments);
      }, delay);
    }
  };
}
  • 节流函数(Throttle)

节流函数是每隔n秒事件执行一次,目的是降低事件执行的频率,比如鼠标移动事件、窗口resize事件等。

function throttle(func, delay) {
  let timerId;
  return function () {
    if (!timerId) {
      timerId = setTimeout(() => {
        func.apply(this, arguments);
        clearTimeout(timerId);
        timerId = null;
      }, delay);
    }
  };
}
  • 函数柯理化(Currying)

函数柯理化可以将一个接收到个参数的函数,转换为多个接收一个参数的函数,可以提高函数的复用。

function curry(fn) {
  return function curried() {
    return fn.apply(this, arguments);
  };
}

const add = curry(function (a, b) {
  return a + b;
});
  • 模块化(Module)

闭包可以将过程封装到函数内部,只暴露必要的接口。

// 闭包模块化
function module() {
  let count = 0;

  function add(x) {
    return (count += x);
  }

  function subtract(x) {
    return (count -= x);
  }

  function getCount() {
    return count;
  }
  return {
    add,
    subtract,
    getCount,
  };
}

闭包的缺点

不是所有的闭包都会造成内存泄漏,内存泄漏是指没被使用的变量没有被销毁,因此当不再使用闭包的时候要注意清理引用。

function counter() {
  let count = 0;
  return function () {
    return ++count;
  };
}

const addCount = counter();
console.log(addCount()); // 1
console.log(addCount()); // 2

addCount = null; // 清理闭包引用