闭包(简单理解)

闭包的学习

  • 闭包就是一个函数引用另外一个函数的变量,因为变量被引用着所以不会被回收,因此可以用来封装一个私有变量。这是优点也是缺点,不必要的闭包只会徒增内存消耗!另外使用闭包也要注意变量的值是否符合你的要求,因为他就像一个静态私有变量一样。闭包通常会跟很多东西混搭起来,接触多了才能加深理解,这里只是开个头说说基础性的东西。

一个小小的实例

function buildList(list) {
 var result = [];
 for(var i = 0; i < list.length; i++) {
  var item = 'item' + list[i];
  result.push( 
    (function(i) {
      console.log(item + ' ' + list[i]);
    })(i)
   );
 }
 return result;
}
 
var fnlist = buildList([1,2,3]);

内存泄漏

其实呢,大家所熟知的闭包会造成内存泄漏,就是内存被占用,无法腾出空间做其他的操作。这就牵扯到了浏览器的垃圾回收机制,下面就来说说垃圾回收机制到底是什么东西。

浏览器的垃圾回收机制分为两种;

  1. 其一:标记清除。具体就是当浏览器运行js时,会给所有的变量,或者占用空间内存的其它东西全部加上标记(就是一个记号,不需要过多研究),执行中当变量被引用到,该变量的标记会被去掉,到最后还存有标记的变量说明已经没有用了,浏览器会把它清除掉,并且收回占用的空间,浏览器会不断地重复执行,所以当变量不用时最终还是会被清除掉,,。而闭包里面的变量受到保护,处于一直被占用状态,也就标记不上了,在内存中也就一直占用
  2. 其二:引用计数。引用计数的含义是跟踪记录每个值被引用的次数,当声明一个变量并将一个引用类型的值赋给该变量时,这个时候的引用类型的值就会是引用次数+1了。如果同一个值又被赋给另外一个变量,则该值的引用次数又+1。
    相反如果包含这个值的引用的变量又取得另外一个值,即被重新赋了值,那么这个值的引用就减一。当这个值的引用次数编程0时,表示没有用到这个值,这个值也无法访问,因此环境就会收回这个值所占用的内存空间回收。这样,当垃圾收集器下次再运行时,它就会释放引用次数为0的值所占用的内存。

如果看到这里还不知道闭包的内存泄漏的话,只有一个原因,那就是闭包内部变量的作用域,还不是很清楚。

### 闭包的概念 在 JavaScript 中,**闭包(Closure)** 是指一个函数能够访问并记住其词法作用域,即使该函数在其作用域外执行。简单来说,闭包是指内部函数可以访问外部函数的变量和参数,即使外部函数已经返回。这种机制使得函数可以在调用后仍然保持对外部作用域中变量引用[^1]。 闭包的核心特性在于它可以“捕获”定义时所处的作用域,并在后续执行时继续使用这个作用域中的变量。JavaScript 的执行环境本身就是一个作用域链,每个函数都可以访问其父级作用域中的变量,这构成了闭包的基础[^3]。 例如: ```javascript function outerFunction() { let count = 0; function innerFunction() { count++; console.log(count); } return innerFunction; } const counter = outerFunction(); counter(); // 输出 1 counter(); // 输出 2 ``` 在这个例子中,`innerFunction` 是一个闭包,它保留了对 `count` 变量引用,即使 `outerFunction` 已经执行完毕。每次调用 `counter()`,都会修改并输出 `count` 的值[^4]。 ### 闭包的作用 闭包在 JavaScript 中具有多种重要作用: 1. **封装私有变量** - 闭包可以用于创建私有变量和方法,避免全局污染。通过将变量限制在外部函数的作用域中,只有内部函数可以访问这些变量,从而实现数据隐藏。 - 示例: ```javascript function createCounter() { let count = 0; return function () { return ++count; }; } const counter = createCounter(); console.log(counter()); // 输出 1 console.log(counter()); // 输出 2 ``` 在此示例中,`count` 是一个私有变量,只能通过返回的函数进行修改和访问。 2. **函数工厂与高阶函数** - 闭包可用于生成具有不同配置的函数实例。例如,可以创建一个通用的计数器工厂,根据不同的初始值生成独立的计数器。 - 示例: ```javascript function createCounter(start) { return function () { return start++; }; } const counter1 = createCounter(5); const counter2 = createCounter(10); console.log(counter1()); // 输出 5 console.log(counter2()); // 输出 10 ``` 这两个计数器分别维护自己的状态,互不干扰[^4]。 3. **事件处理与回调函数** - 闭包广泛应用于事件监听和异步编程中,确保回调函数可以访问定义时的上下文。 - 示例:节流函数控制滚动加载 ```javascript function throttle(fn, delay) { let valid = true; return function () { if (valid) { setTimeout(() => { fn.apply(this, arguments); valid = true; }, delay); valid = false; } }; } window.addEventListener("scroll", throttle(function () { console.log("页面正在滚动"); }, 1000)); ``` 上述代码中,`throttle` 函数利用闭包保存了 `valid` 状态,确保在指定的时间间隔内只执行一次回调[^5]。 4. **模块模式与单例设计** - 闭包常用于实现模块化开发模式,提供私有属性和方法,并暴露有限的公共接口。 - 示例: ```javascript const CounterModule = (function () { let count = 0; function increment() { count++; } function getCount() { return count; } return { increment, getCount }; })(); CounterModule.increment(); console.log(CounterModule.getCount()); // 输出 1 ``` 此示例中,`count` 是私有的,无法直接访问或修改,只能通过暴露的方法进行操作[^1]。 ### 闭包的使用场景 1. **数据缓存与记忆化函数** - 利用闭包可以缓存函数的计算结果,避免重复计算,提高性能。 - 示例: ```javascript function memoize(fn) { const cache = {}; return function (arg) { if (cache[arg]) { return cache[arg]; } const result = fn(arg); cache[arg] = result; return result; }; } function factorial(n) { if (n === 0) return 1; return n * factorial(n - 1); } const memoizedFactorial = memoize(factorial); console.log(memoizedFactorial(5)); // 输出 120 console.log(memoizedFactorial(5)); // 直接从缓存读取 ``` 2. **定时器与异步操作** - 闭包在 `setTimeout` 和 `setInterval` 中非常常见,确保在异步执行时仍能访问正确的上下文。 - 示例: ```javascript for (var i = 1; i <= 5; i++) { setTimeout(function () { console.log(i); // 所有输出都是6 }, i * 1000); } ``` 由于 `var` 没有块级作用域,所有 `setTimeout` 共享同一个 `i`。可以通过闭包解决这个问题: ```javascript for (let i = 1; i <= 5; i++) { setTimeout(function () { console.log(i); // 正确输出1到5 }, i * 1000); } ``` 或者使用 IIFE(立即调用函数表达式)显式创建闭包: ```javascript for (var i = 1; i <= 5; i++) { (function (i) { setTimeout(function () { console.log(i); }, i * 1000); })(i); } ``` 3. **函数柯里化与偏应用** - 闭包支持将多参数函数转换为一系列接受单个参数的函数,提升函数的复用性。 - 示例: ```javascript function add(a) { return function (b) { return a + b; }; } const add5 = add(5); console.log(add5(3)); // 输出 8 console.log(add5(10)); // 输出 15 ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值