全方位讲解 " 闭包 "

686 阅读2分钟

前言

闭包是由函数以及声明该函数的词法环境组合而成,该环境包含闭包创建时作用域内的任何局部变量。 —— 来源 MDN 最新解释🤞

特点

  1. 闭包是一个函数。
  2. 在外部函数上下文销毁的情况下,仍能访问外部函数作用域中的自由变量。 例子
function say() {
    let name = '瑾行'
    function hello() {
        console.log('hello,' + name)
    }
   return hello
}
let func = say()
func() // hello, 瑾行

由案例可知,当let func = say()执行结束,虽然say函数的执行上下文已被回收销毁,但执行结果依然可以读取到name变量值。

闭包与作用域链

按道理来说,当执行上下文被销毁后是读取不到变量值的。但不要忘记,创建函数执行上下文的同时也维护了一条作用域链。当闭包函数引用外层函数的局部变量时,即使外层函数上下文被销毁了,但JavaScript依然会让引用的活动变量对象存在内存中,闭包函数依然可以通过作用域链寻找到它。

应用

防抖与节流,以防抖为例。

function debounce(fn, delay) {
  let timer
  return function() {
    if(timer) clearTimeout(timer)
    timer = setTimeout(() => {
      fn.apply(this, arguments)
    }, delay)
  }
}

私有化变量

let obj = (function() {
  let name = '瑾行';
  function getName() {
    console.log('name:', name)
  }
  return {
    getName: getName
  }
})()
console.log(obj.name) // undefined
obj.getName() // '瑾行'

性能

若非特定任务需要使用闭包,在函数中创建函数是不明智的,因为闭包在处理速度和内存消耗对脚本具有负面影响。

若创建新的对象或者类,方法通常应该关联对象的原型,而不是定义到对象的构造器中。因为每次构造器被调用,方法都会被重新赋值一次。

例子

function func(name) {
    this.name = name.toString()
    this.getName = function() {
        return this.name
    }
}

在上述代码,没有利用闭包的好处,应修改如下:

function func(name) {
    this.name = name.toString()
}
func.prototype = {
    getName: function() {
        return this.name
    }
}

参考

闭包
JavaScript深入之闭包