前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >前端开发中真的没用到闭包嘛?九个日常案例了解常用闭包

前端开发中真的没用到闭包嘛?九个日常案例了解常用闭包

作者头像
肥晨
发布2025-02-27 14:20:31
发布2025-02-27 14:20:31
7300
代码可运行
举报
文章被收录于专栏:农民工前端农民工前端
运行总次数:0
代码可运行

导文

很多前端都感觉自己没用过闭包,当面试官问到:

“现实开发中哪里会用到闭包”

直接哑语。

我们真的没用到闭包嘛?

闭包是什么?

闭包(Closure) 是 JavaScript 中一个非常重要的概念,它涉及到函数、作用域以及变量的生命周期。简单来说,闭包是一个函数,它能够记住并访问定义时的作用域,即使这个函数在外部作用域执行时,仍然能够访问到原本定义时的局部变量。

闭包的定义

闭包的形成条件是:函数内部引用了外部函数的变量,并且这个函数在外部被调用时依然能够访问这些外部变量。换句话说,闭包是一个 “函数 + 词法作用域” 的组合。

闭包的基本特性

  1. 访问外部变量:闭包允许内部函数访问外部函数的局部变量。
  2. 变量的持久性:即使外部函数已经执行完毕,闭包中的内部函数仍然可以访问外部函数中的变量。

举个例子:

代码语言:javascript
代码运行次数:0
复制
function outer() {
  let counter = 0;

  // 内部函数
  function inner() {
    counter++;
    console.log(counter);
  }

  return inner;
}

const myClosure = outer(); // 返回了一个闭包
myClosure(); // 输出: 1
myClosure(); // 输出: 2
解释:
  • outer 是外部函数,它定义了一个局部变量 counter,并返回了内部函数 inner
  • inner 是一个闭包,它引用了外部函数 outer 中的 counter 变量。
  • 即使外部函数 outer 执行完毕,闭包 inner 仍然能够访问到 counter 变量,保持 counter 的状态。

闭包的注意事项:

  • 内存泄漏:闭包能够访问外部函数的局部变量,但如果闭包持有的变量过多且没有释放,可能导致内存泄漏。为了避免这种情况,应注意管理闭包的生命周期。
  • 变量的共享问题:如果在多个闭包中共享变量,可能会产生预期之外的行为,因此要小心作用域链的使用。

总之,闭包是 JavaScript 中非常强大和灵活的特性,它通过保持对外部变量的引用,使得函数的行为更加灵活和强大。

日常开发中哪些是用到闭包了呢?

在日常开发中,闭包的应用非常广泛,下面列举一些常见的场景。

1. 定时器和延迟执行

setTimeoutsetInterval 中,常常使用闭包来记住当前的变量或状态,进行延迟操作。

例如:

代码语言:javascript
代码运行次数:0
复制
function countdown(seconds) {
  let timeLeft = seconds;
  const timer = setInterval(function() {
    console.log(timeLeft);
    timeLeft--;
    if (timeLeft <= 0) {
      clearInterval(timer);
      console.log("Time's up!");
    }
  }, 1000);
}

countdown(5);

这里,setInterval 中的回调函数是一个闭包,它访问了外部函数 countdown 中的 timeLeft 变量。

2. 防抖和节流(Debouncing & Throttling)

在处理大量的事件(如 scrollresizekeypress)时,常使用闭包来实现防抖(Debounce)和节流(Throttle),从而避免频繁执行。

防抖示例

代码语言:javascript
代码运行次数:0
复制
function debounce(func, delay) {
  let timeout;
  return function() {
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      func.apply(this, arguments);
    }, delay);
  };
}

const log = debounce(() => console.log('Function executed!'), 1000);

window.addEventListener('resize', log);

这里,debounce 函数返回一个闭包,确保在用户停止触发事件后的延迟时间内才会执行实际的回调函数。

3. 回调函数与异步编程

在使用回调函数的异步编程中,闭包可以保持对外部变量的访问,确保异步操作完成时,能正确访问这些变量。

例如,异步请求中的闭包使用:

代码语言:javascript
代码运行次数:0
复制
function fetchData(url) {
  let dataLoaded = false;
  // 假设 fetch 请求完成后会调用回调函数
  fetch(url)
    .then(response => response.json())
    .then(data => {
      dataLoaded = true;
      console.log('Data fetched:', data);
    });

  return function() {
    if (dataLoaded) {
      console.log('Data has been loaded');
    } else {
      console.log('Loading data...');
    }
  };
}

const checkDataStatus = fetchData('https://api.example.com/data');
checkDataStatus(); // 可能输出: 'Loading data...'

这里,checkDataStatus 函数是一个闭包,它能访问 fetchData 中的 dataLoaded 变量。

4. 模块模式(Module Pattern)

闭包广泛应用于 JavaScript 模块化开发,用于将变量和方法封装在函数作用域内,避免污染全局作用域。

例如:

代码语言:javascript
代码运行次数:0
复制
const module = (function() {
  let privateVar = 'I am private';

  function privateMethod() {
    console.log(privateVar);
  }

  return {
    publicMethod: function() {
      privateMethod();
    }
  };
})();

module.publicMethod(); // 输出: I am private

在这个例子中,privateVarprivateMethod 是私有的,外部只能访问公开的 publicMethod。 在 Vue.js 中,闭包的使用并不像在 JavaScript 中直接调用函数时那么显而易见,但仍然有一些地方可以体现闭包的特性,主要体现在事件处理、计算属性、生命周期钩子和组件方法中。以下是 Vue 中一些常见的闭包应用场景:

5. ** Vue事件处理中的闭包**

在 Vue 中,经常会使用事件处理方法。由于 Vue 会自动处理事件绑定,这时方法本身可能会变成一个闭包,能够访问它的外部作用域中的变量。

例如,假设有一个按钮,点击时增加一个计数值:

代码语言:javascript
代码运行次数:0
复制
<template>
  <button @click="increment">Click me</button>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    };
  },
  methods: {
    increment() {
      this.count++;
      console.log(this.count);
    }
  }
};
</script>

在这个例子中,increment 方法是一个闭包,它可以访问 Vue 组件的 data 中的 count 变量,即使该方法是通过 @click 事件触发的。即使 increment 方法和事件处理函数被分离,它仍然能访问到 this.count,因为它保存了对外部作用域的引用。

6. 计算属性中的闭包

计算属性是 Vue 中的一个重要特性,它可以依赖其他数据,并在依赖的数据变化时自动重新计算。计算属性背后也有闭包的实现:每当计算属性被访问时,它会闭包保存相关的响应式依赖,直到计算属性的值被更新。

代码语言:javascript
代码运行次数:0
复制
<template>
  <div>
    <p>{{ doubledCount }}</p>
    <button @click="count++">Increase Count</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    };
  },
  computed: {
    doubledCount() {
      return this.count * 2;
    }
  }
};
</script>

在这个例子中,doubledCount 是一个计算属性,它闭包保存了 this.count 的依赖关系。每当 this.count 发生变化时,doubledCount 会自动重新计算。

7. 生命周期钩子函数中的闭包

Vue 组件中的生命周期钩子,如 createdmountedupdated 等,通常会引用组件的数据或方法,因此它们本质上也是闭包。这意味着即使在组件的生命周期内某些状态发生变化,这些钩子函数仍能访问到组件的上下文。

代码语言:javascript
代码运行次数:0
复制
<template>
  <div>
    <p>{{ message }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: "Hello, Vue!"
    };
  },
  mounted() {
    setTimeout(() => {
      this.message = "Hello, World!"; // 这里的箭头函数是闭包
    }, 1000);
  }
};
</script>

这里,mounted 钩子内的箭头函数是一个闭包,它能够访问到 this.message,即使 setTimeout 是异步执行的。

8. 方法中的闭包

Vue 方法(特别是在 methods 中定义的那些)可能会变成闭包,并且这些方法可以访问到组件实例中的所有数据和方法。在异步操作中,闭包特别有用,因为它们能保证访问到正确的上下文(即 this)。

代码语言:javascript
代码运行次数:0
复制
<template>
  <div>
    <p>{{ message }}</p>
    <button @click="delayedChangeMessage">Change message</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: "Initial message"
    };
  },
  methods: {
    delayedChangeMessage() {
      setTimeout(() => {
        this.message = "Updated message";  // 闭包保存了对this的引用
      }, 2000);
    }
  }
};
</script>

这里,delayedChangeMessage 方法内的箭头函数是闭包,它能够引用到 this.message,即使延迟了两秒钟才执行。

9. vue v-for 循环中的闭包

在 Vue 的 v-for 循环中,也可能会使用闭包,尤其是当每个循环项都绑定了事件时。循环中的每个函数都会形成闭包,保存其循环上下文。

代码语言:javascript
代码运行次数:0
复制
<template>
  <div>
    <ul>
      <li v-for="(item, index) in items" :key="index" @click="showItem(index)">
        {{ item }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      items: ['Item 1', 'Item 2', 'Item 3']
    };
  },
  methods: {
    showItem(index) {
      alert('Item index: ' + index);
    }
  }
};
</script>

在上面的代码中,showItem 方法通过 v-for 中的每个 li 元素绑定了事件监听器。每个事件处理程序都是一个闭包,它可以访问到 index 变量,即使这些事件处理函数是在不同的上下文中调用的。

还有没有其他常有的?欢迎各位大佬在评论区留言讨论

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-02-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 农民工前端 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 导文
  • 闭包是什么?
    • 闭包的定义
    • 闭包的基本特性
    • 举个例子:
    • 闭包的注意事项:
  • 日常开发中哪些是用到闭包了呢?
    • 1. 定时器和延迟执行
    • 2. 防抖和节流(Debouncing & Throttling)
    • 3. 回调函数与异步编程
    • 4. 模块模式(Module Pattern)
    • 5. ** Vue事件处理中的闭包**
    • 6. 计算属性中的闭包
    • 7. 生命周期钩子函数中的闭包
    • 8. 方法中的闭包
    • 9. vue v-for 循环中的闭包
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档