前端-打卡每日面试题-闭包(2024.1.25)

一、闭包是什么?

1、概念

闭包是指在函数内部定义的函数,能够访问到外部函数的变量,并且保持对这些变量的引用,即使外部函数已经执行完毕。闭包形成了一个封闭的作用域,使得内部函数可以访问外部函数的局部变量,从而延长了这些变量的生命周期。

function outer() {
  let x = 10;

  function inner() {
    console.log(x); // 内部函数可以访问外部函数的变量 x
  }

  return inner; // 返回内部函数
}

const closureFunction = outer(); // 调用外部函数,并将内部函数保存在变量中
closureFunction(); // 执行保存的内部函数,依然可以访问外部函数的变量 x

二、为什么有闭包?

闭包在 JavaScript 中的存在是因为 JavaScript 采用了词法作用域的机制。词法作用域指的是变量的作用域是在代码编写阶段就确定的,而不是在执行阶段确定的。

当函数被定义时,它会创建一个闭包,保存了函数内部的局部变量和当前作用域链。这个闭包使得函数能够在定义它的词法作用域之外的地方被调用时,仍然能够访问到其内部的变量。

个人(理解记忆)

这个应该是牵扯到作用域的概念,一个内部函数无法访问到外部函数定义的变量,在没有闭包的情况下,函数只能访问其定义时的作用域,而无法在定义后继续访问外部作用域的变量。

比如,你进了一个房间(外部函数),在房间里有一个特殊的盒子(内部函数),你把一些东西放进这个盒子里。

当你走出房间,房间的大门关闭了(外部函数执行完毕),但是这个盒子里的东西你还能记得,而且你可以随时打开这个盒子,取出里面的东西。这个魔法盒子就好比闭包,记住了外部函数的一些东西,就算外部函数执行完毕了,闭包还能保留这些记忆

function outer() {
  let x = 10;

  function inner() {
    console.log(x); // 内部函数尝试访问外部函数的变量 x,但会报错
  }

  inner(); // 直接调用内部函数,无法访问外部函数的变量 x
}

outer();

三、闭包的优点和缺点

优点:

1. 数据封装

闭包允许将变量隐藏在内部函数中,只暴露必要的接口,提高代码的安全性和可维护性。

function createCounter() {
  let count = 0;

  return function() {
    count++;
    console.log(count);
  };
}

const counter = createCounter();
counter(); // 输出: 1
counter(); // 输出: 2
2. 保持状态

闭包可以保持外部函数调用时的状态,使得状态在多次调用之间得以保留

function createIncrementer(initialValue) {
  let value = initialValue;

  return function() {
    value++;
    console.log(value);
  };
}

const incrementByTwo = createIncrementer(2);
incrementByTwo(); // 输出: 3
incrementByTwo(); // 输出: 4
3. 实现局部变量

通过闭包,可以模拟实现局部变量的效果,创建一个局部作用域。

function outer() {
  let outerVariable = 'I am from outer';

  function inner() {
    let innerVariable = 'I am from inner';
    console.log(outerVariable); // 访问外部变量
    console.log(innerVariable); // 访问内部变量
  }

  inner();
}

outer();
4. 函数工厂

闭包允许创建函数工厂,即动态生成具有特定行为的函数

function multiplier(factor) {
  return function(x) {
    return x * factor;
  };
}

const double = multiplier(2);
console.log(double(5)); // 输出: 10
缺点 :
  1. 内存消耗: 闭包中的变量不会被垃圾回收,直到闭包不再被引用。如果闭包中包含大量变量或者循环引用,可能导致内存泄漏。

  2. 性能问题: 由于闭包涉及维护外部函数的作用域链,可能会导致性能损失,尤其是在嵌套层次较深或循环中。

  3. 滥用导致的可读性降低: 过度使用闭包可能导致代码的可读性下降。程序员需要理解整个作用域链,以便正确理解变量的值和生命周期。

  4. 可能导致意外的行为: 如果不小心修改了闭包中的变量,可能导致意外的行为,因为这些变量在外部函数执行完毕后仍然存在。

  5. 维护困难: 在大型项目中,如果滥用闭包,可能导致难以维护和调试的代码。特别是在多人协作的项目中,过度使用闭包可能增加理解的难度。

四、项目中的使用

防抖、节流、柯里化函数都是闭包

防抖(Debouncing): 防抖是一种技术,用于限制一段时间内函数的调用次数。通常在处理输入框输入、滚动等频繁触发的事件时使用。通过使用闭包来存储计时器,可以确保在一定时间内只执行最后一次函数调用。

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

节流(Throttling): 节流是一种控制函数调用频率的技术,确保一定时间内只执行一次函数。也可以使用闭包来存储状态,例如上一次函数调用的时间戳

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

柯里化(Currying): 柯里化是将一个多参数函数转化为一系列单参数函数的过程。通过使用闭包,可以存储每个部分函数的参数,直到所有参数都被收集完成,然后执行原始函数。

function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      return function(...moreArgs) {
        return curried.apply(this, args.concat(moreArgs));
      };
    }
  };
}

五、总结(以上便于理解记忆)

闭包的概念?怎么形成闭包?闭包的优缺点?项目中哪里使用闭包?

闭包就是函数嵌套函数被嵌套的函数叫做闭包函数。外部函数里面嵌套一个内部函数,外部函数在定义一个变量,再将内部函数作为整体作为返回值返回出去,外部定义一个全局变量接受,就能是这个变量生命周期延长,形成闭包。闭包的优点就是有:

  1. 数据封装: 闭包可以用于创建私有变量,实现数据封装,防止外部直接访问内部变量。
  2. 保持状态: 闭包可以保持外部函数调用时的状态,例如计数器等。
  3. 实现柯里化和高阶函数: 闭包支持函数式编程的概念,实现柯里化和高阶函数等功能。

缺点:

  1. 内存泄漏: 如果不适当使用,闭包可能导致内存泄漏,因为它会保留外部函数的整个作用域链。
  2. 性能问题: 闭包涉及维护作用域链,可能导致性能问题,尤其是在嵌套较深的情况下。
  3. 可读性降低: 过度使用闭包可能导致代码可读性降低,因为读者需要理解整个作用域链。

项目中闭包通常用于实现私有变量、保持状态或者创建工厂函数,或者防抖节流。 

你可能感兴趣的:(前端,面试,职场和发展,uni-app,小程序,设计模式,学习)