求star

开源不易,喜欢请点个star吧

Ocean Han
907 字
5 分钟
Js中的闭包
2022-08-05

什么是闭包?为什么要用闭包?闭包的工作方式?闭包的常见应用?

什么是闭包#

:::tip 闭包的概念

函数和对其词法环境lexical environment的引用捆绑在一起构成闭包

​ 也就是说,闭包可以让内部函数访问外部函数的作用域。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。

:::

最简单的例子#

function compare(propertyName) {
    	function(obj1,obj2){
            let value1 = obj1[propertyName]
            let value2 = obj2[propertyName]
            
            return (value1 - value2 < 0)
        }
}
TIP

​ 在这个例子中,内部的匿名函数中的变量value1和value2,引用了外部函数的propertyName变量

闭包的特点#

:::tip 特点

  • 函数嵌套函数
  • 函数内部可以引用函数外部的参数和变量
  • 参数和变量不会被垃圾回收机制回收

:::

闭包的一些应用#

1. 使用闭包模拟私有方法#

TIP

​ 使用闭包来定义公共函数,并令其可以访问私有函数和变量,这种方式也叫模块方式

​ 两个计数器counter1和counter2是相互独立的

​ 其它例如计数器、延迟调用、回调等闭包的应用,其核心思想还是创建私有变量和延长变量的生命周期

const Counter = function () {
  let privateCounter = 0
  function changeBy(val) {
    privateCounter += val
  }
  return {
    increment: function () {
      changeBy(1)
    },
    decrement: function () {
      changeBy(-1)
    },
    value: function () {
      return privateCounter
    }
  }
}

const counter1 = new Counter()
const counter2 = new Counter()
counter1.increment()
counter1.increment()
console.log(counter1.value());  //  2
console.log(counter2.value());  //  0
// ...

2. 函数柯里化#

TIP

​ 柯里化的目的在于避免频繁调用具有相同参数函数的同时,又能够轻松的重用

function getArea(width, height) {
  return width * height
}

const area1 = getArea(10, 20)
const area2 = getArea(10, 30)
const area3 = getArea(10, 40)

// 如果我们碰到的长方形的宽老是10,我们可以使用闭包柯里化这个函数
function getArea(width) {
  return (height) => {
    return width * height
  }
}

const getTenWidthArea = getArea(10)
// 之后碰到宽度为10的长方形就可以这样计算面积
const area = getTenWidthArea(20)
// 而且如果遇到宽度偶尔变化也可以轻松复用
const getTwentyWidthArea = getArea(20)

3. 定时器和闭包#

for(var i = 0;i<5;i++){
    setTimeout(()=> {
        console.log(i)
    },100)
}
// 结果是输出 5 次 5
WARNING

​ 先来看一个例子,这个例子大家应该都知道为什么

var只有函数作用域,而{}相当于形成了一个块作用域,所以5次循环用的是同一个i,再加上for循环内部的seTimeout还是一个异步任务,所以在执行seTimeout的回调之前,i就先自增到了5

解决:

  • 最简单的解决办法可以将var改为ES6中的let
  • 引入闭包来保存变量(立即执行函数)
for(var i = 0;i<5;i++){
    (function(i) {
        setTimeout(() => {
            console.log(i)
        },100)
    })(i)
}
// 0 1 2 3 4

如果想每隔100毫秒依次输出,而不是同时:

for (var k = 0; k < 5; k++) {
  (function(k) {
    setTimeout(() => {
      console.log(k)
    }, 100 * k)
  })(k)
}

闭包的使用场景#

TIP
  • 创建私有变量
  • 延长变量的生命周期

闭包的总结#

:::tip 优点

  1. 可以访问到函数内部的局部变量
  2. 可以避免全局变量的污染
  3. 这些变量的值始终保持在内存中,不会在外层函数调用后被自动清除。

:::

:::danger 缺点

会增大内存使用量,滥用闭包会影响性能,导致内存泄漏等问题。

在退出函数之前,将不使用的局部变量全部删除,可以使变量赋值为null,这样就会被回收.#

:::

Js中的闭包
https://blog.oceanh.top/posts/frontend/闭包/
作者
Ocean Han
发布于
2022-08-05
许可协议
CC BY-NC-SA 4.0
最后修改时间
2024-08-10 10:08:49