闭包的基本定义

如果一个函数访问了此函数的父级及父级以上的作用域变量,那么这个函数就是一个闭包。闭包会创建一个包含外部函数作用域变量的环境,并将其保存在内存中,这意味着,即使外部函数已经执行完毕,闭包仍然可以访问和使用外部函数的变量。

 1
 2function fn1() {
 3  let a = 1;
 4  function fn2() {
 5    a++;
 6    console.log(a);
 7  }
 8  return fn2;
 9}
10const fn2 = fn1();
11
12fn2() 
13fn2() 

闭包的优缺点及特性

  • 闭包的优点
  1. 保护变量:闭包可以将变量封装在函数内部,避免全局污染,保护变量不被外部访问和修改。
  2. 延长变量生命周期:闭包使得函数内部的变量在函数执行完后仍然存在,可以在函数外部继续使用。
  3. 实现模块化:闭包可以创建私有变量和私有方法,实现模块化的封装和隐藏,提高代码的可维护性和安全性。
  4. 保持状态:闭包可以捕获外部函数的变量,并在函数执行时保持其状态。这使得闭包在事件处理、回调函数等场景中非常有用。
  • 闭包的缺点
  1. 内存占用:闭包会导致外部函数的变量无法被垃圾回收,从而增加内存占用。如果滥用闭包,会导致内存泄漏问题。
  2. 性能损耗:闭包涉及到作用域链的查找过程,会带来一定的性能损耗。在性能要求高的场景下,需要注意闭包的使用。
  • 闭包的特性
  1. 函数嵌套:闭包的实现依赖于函数嵌套,即在一个函数内部定义另一个函数。
  2. 记忆外部变量:闭包可以记住并访问外部函数的变量,即使外部函数已经执行完毕。
  3. 延长作用域链:闭包将外部函数的作用域链延长到内部函数中,使得内部函数可以访问外部函数的变量。
  4. 返回函数:闭包通常以函数的形式返回,使得外部函数的变量仍然可以被内部函数引用和使用。

自执行函数

 1let say = (function(){
 2  let val = 'hello world';
 3  function say(){
 4    console.log(val);
 5  }
 6  return say;
 7})()

节流防抖

 1
 2function throttle(func, delay) {
 3  let timer = null;
 4  return function () {
 5    if (!timer) {
 6      timer = setTimeout(() => {
 7        func.apply(this, arguments);
 8        timer = null;
 9      }, delay);
10    }
11  };
12}
13
14
15function debounce(func, delay) {
16  let timer = null;
17  return function () {
18    clearTimeout(timer);
19    timer = setTimeout(() => {
20      func.apply(this, arguments);
21    }, delay);
22  };
23}

函数柯里化

  • 函数柯里化(Currying)是一种将多个参数的函数转换为一系列接受单个参数的函数的过程。举个简单的例子,我们有一个原始函数add(a, b, c),我们可以将它柯里化为addCurried(a)(b)(c)的形式。
 1
 2function add(a, b, c) {
 3  return a + b + c;
 4}
 5console.log(add(1, 2, 3)); 
 6
 7function addCurried1(a) {
 8  return function (b) {
 9    return function (c) {
10      return a + b + c;
11    };
12  };
13}
14
15const addCurried2 = (a) => (b) => (c) => a + b + c;
16console.log(addCurried1(1)(2)(3)); 
17console.log(addCurried2(1)(2)(3)); 

链式调用

  • 利用闭包原理封装一个简单的计算器
 1function calculator() {
 2  let result = 0;
 3
 4  function add(num) {
 5    result += num;
 6    return this;
 7  }
 8
 9  function subtract(num) {
10    result -= num;
11    return this;
12  }
13
14  function multiply(num) {
15    result *= num;
16    return this;
17  }
18
19  function divide(num) {
20    result /= num;
21    return this;
22  }
23
24  function getResult() {
25    return result;
26  }
27
28  function clear() {
29    result = 0;
30    return this;
31  }
32
33  return {
34    add,
35    subtract,
36    multiply,
37    divide,
38    getResult,
39    clear,
40  };
41}
42const calc = calculator();
43const result = calc.add(5).subtract(2).divide(3).multiply(6).getResult();
44console.log(result); 

迭代器

 1function createIterator(arr) {
 2  let index = 0;
 3
 4  return {
 5    next: function() {
 6      if (index < arr.length) {
 7        return {
 8          value: arr[index++],
 9          done: false
10        };
11      } else {
12        return {
13          done: true
14        };
15      }
16    }
17  };
18}
19
20const myIterator = createIterator([1, 2, 3]);
21
22console.log(myIterator.next()); 
23console.log(myIterator.next()); 
24console.log(myIterator.next()); 
25console.log(myIterator.next()); 

发布-订阅模式

 1function createPubSub() {
 2  
 3  const subscribers = {};
 4
 5  
 6  function subscribe(event, callback) {
 7    
 8    if (!subscribers[event]) {
 9      subscribers[event] = [];
10    }
11    
12    subscribers[event].push(callback);
13  }
14
15  
16  function publish(event, data) {
17    
18    if (!subscribers[event]) {
19      return;
20    }
21    
22    subscribers[event].forEach((callback) => {
23      callback(data);
24    });
25  }
26
27  
28  return {
29    subscribe,
30    publish,
31  };
32}
33
34
35const pubSub = createPubSub();
36
37
38pubSub.subscribe("event1", (data) => {
39  console.log("订阅者1收到事件1的数据:", data);
40});
41
42pubSub.subscribe("event2", (data) => {
43  console.log("订阅者2收到事件2的数据:", data);
44});
45
46
47pubSub.publish("event1", "Hello");
48
49
50pubSub.publish("event2", "World");
51
52

闭包中的内存泄漏指的是在闭包函数中,由于对外部变量的引用而导致这些变量无法被垃圾回收机制释放的情况。当一个函数内部定义了一个闭包,并且这个闭包引用了外部变量时,如果这个闭包被其他地方持有,就会导致外部变量无法被正常释放,从而造成内存泄漏。
解决闭包中的内存泄漏问题通常需要注意解除外部变量和闭包函数的引用,以及解绑函数本身的引用,使得闭包中引用的外部变量和闭包函数能够被垃圾回收机制释放。

  • 以下是使用闭包时解决内存泄漏的示例
 1function createClosure() {
 2  let value = 'Hello';
 3
 4  
 5  var closure = function() {
 6    console.log(value);
 7  };
 8
 9  
10  var releaseClosure = function() {
11    value = null; 
12    closure = null; 
13    releaseClosure = null; 
14  };
15
16  
17  return {
18    closure,
19    releaseClosure
20  };
21}
22
23
24var closureObj = createClosure();
25
26
27closureObj.closure(); 
28
29
30closureObj.releaseClosure();
31
32
33closureObj.closure(); 

如有错误欢迎在评论区指正,一起讨论!(๑•̀ㅂ•́)و✧

个人笔记记录 2021 ~ 2025