📜  描述 JavaScript 中的闭包概念(1)

📅  最后修改于: 2023-12-03 15:39:50.801000             🧑  作者: Mango

描述 JavaScript 中的闭包概念

什么是闭包

闭包是指函数在访问父级函数作用域变量时,形成的内部函数和该内部函数所在的环境的组合。简单来说,闭包可以让内部函数访问外部函数的作用域。

闭包的实现方式

JavaScript中的函数是一等公民,因此可以将函数作为参数传递,也可以将函数作为返回值。当函数作为返回值时,就很容易形成闭包。

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

let counter = outer();
counter(); // 1
counter(); // 2

上述代码中,outer函数返回了inner函数,由于inner函数仍然可以访问outer函数的作用域,因此count变量在inner函数中仍然可用。通过调用outer函数返回的counter函数,可以形成闭包,每次调用时count都会自增1。

闭包的应用
封装变量

闭包可以让变量不被外界所访问,从而实现变量的封装。

function createCounter() {
  let count = 0;
  return {
    increment: function() {
      count++;
      console.log(count);
    },
    reset: function() {
      count = 0;
      console.log(count);
    }
  }
}

let counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.reset(); // 0

上述代码中,createCounter函数返回了一个包含两个方法(incrementreset)的对象,这两个方法都可以访问count变量,但是外界无法直接访问变量count

函数柯里化

函数柯里化(Currying)是指将一个多参数的函数转换为一系列单参数的函数的技术,闭包可以很方便地实现函数柯里化。

function add(x) {
  return function(y) {
    return x + y;
  }
}

let add5 = add(5);
console.log(add5(3)); // 8

上述代码中,add函数返回了一个内部函数,内部函数利用闭包访问了外层函数的x变量,从而可以实现对x变量的记忆化,每次调用add返回的内部函数时,都只需要传入一个参数y即可得到结果。

防抖和节流

防抖(Debounce)和节流(Throttle)是对事件频繁触发的一种优化方式,闭包可以很方便地实现防抖和节流。

防抖是指在某个时间段内,如果事件被触发多次,则只执行最后一次。

function debounce(fn, delay) {
  let timeoutId;
  return function() {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => {
      fn.apply(this, arguments);
    }, delay);
  };
}

function handleInput() {
  console.log('input事件被触发');
}

let debouncedInputHandler = debounce(handleInput, 300);
document.querySelector('input').addEventListener('input', debouncedInputHandler);

上述代码中,debounce函数接受一个函数和一个时间间隔作为参数,返回一个函数,该函数在调用时利用闭包保存了timeoutId变量,每当被触发时,都会清除之前的定时器,然后重新设置一个定时器,等待一定时间后再执行传入的函数。

节流是指在某个时间段内,只能触发一次事件。

function throttle(fn, delay) {
  let prevTime = 0;
  return function() {
    let currTime = Date.now();
    if (currTime - prevTime > delay) {
      fn.apply(this, arguments);
      prevTime = currTime;
    }
  };
}

function handleScroll() {
  console.log('scroll事件被触发');
}

let throttledScrollHandler = throttle(handleScroll, 300);
document.addEventListener('scroll', throttledScrollHandler);

上述代码中,throttle函数接受一个函数和一个时间间隔作为参数,返回一个函数,该函数在调用时利用闭包保存了prevTime变量,每当被触发时,都会判断当前时间是否已经超过上一次执行事件的时间加上时间间隔,如果超过了则执行传入的函数,否则不执行。