一起来学习js的闭包

147 阅读3分钟

这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战

什么是闭包

《JavaScript高级程序设计》对闭包的解释,闭包是指有权访问另一个函数作用域中的变量的函数

这概念可能不是很好理解,我们一步步来说明。

我在上一篇文章中说过,js有全局作用域,函数作用域 和 块级作用域。

在函数作用域里面声明的变量,一般只能在函数内部能访问。

function getName () {
  var name = '答案cp3'
  console.log(name) 
}
getName() // 答案cp3
console.log(name) // 打印 ''

可以看到, 在getName函数内部声明的name变量,无法在外部访问到,只能在内部访问。

但是你在外部声明的变量,可以在函数内部访问。

var name = '答案cp3'
function getName () {
  console.log(name) 
}
getName() // 答案cp3
console.log(name) // 答案cp3

所以总结一下:

函数内部可以访问到外部的变量,外部却不能访问到函数内部的变量。

那要怎么样外部才能访问到函数内部的变量?

这时候闭包就准备出场了。

既然函数内部可以访问外部的变量,那么我们就在函数内部再声明一个函数,通过这个函数去取变量,然后把这个函数return

代码如下:

function getName () {
  var name = '答案cp3'
  function fn () {
    return name
  }
  return fn
}
let getNameFn = getName() 
console.log(getNameFn()) // 答案cp3

你没看错,上面代码中,getName内部的fn函数已经形成了一个闭包,它可以访问到getName函数的name变量。

闭包的定义就是这样,你是不是理解了啊?

闭包的运用

讲到这里,可能有人会说,闭包的定义我懂了,但是它可以用在哪呢?

别急,我现在讲。

  1. 防抖/节流函数

这里我以防抖函数为例子:

// 防抖函数
function debounce(fn, delay = 100) {
  let timer = null
  return function (...args) {
    if (timer) clearTimeout(timer)
    timer = setTimeout(() => {
      fn(...args)
    }, delay)
  }
}
// 监听滚动事件
window.addEventListener('scroll', debounce(function () {
  console.log('scroll事件触发了')
}))

可以看到我们的debounce声明了timer变量,这个变量在外部无法访问,只能在内部function里面访问到。

  1. 单例模式
class singleMode {
    constructor(name) {
        this.name = name
    }
    getName () {
       return this.name
    }
    getInstance () {
       return this
    }
}
let createSingleMode = (function () {
  let singleModeInstance
  return function (name) {
   if (!singleModeInstance) singleModeInstance = new singleMode(name)
   return singleModeInstance
  }
})()

在函数内部声明了singleModeInstance变量,只在它内部的函数才能访问它,并返回。

  1. 模拟私有属性,避免直接访问变量
function getInfo () {
  var name = '答案cp3'
  return (function () {
    return {
     getName: function () { return name }
    }
  })()
}
let info = getInfo()
let name = info.getName()
console.log(name) // 答案cp3

不想把name暴露给外层作用域,通过闭包把它只能通过特定的方法调用获取。

闭包的弊端

以上讲了闭包的运用场景,不局限于这些场景,还有很多地方场景有使用到闭包。

那闭包有有没有不好的地方?

有。

由于函数内部变量被它内部的函数所引用,所以就算函数已经被调用完了,但是这个变量还是存在内存中,无法被垃圾回收机制回收。所以如果过多使用闭包有可能导致内存占用过多

所以大家需要使用闭包的时候才使用。

总结

以上就是我讲的js的闭包,闭包还是使用在很多场景下,有时候我们都没察觉到我们使用了闭包,通过今天的文章,希望可以让你对闭包有一定的了解。

感谢你们的阅读。