闭包-保证你看看明明白白

闭包

闭包官方的解释:闭包(closure)是一个函数以及其捆绑的周边环境状态(词法环境)的引用的组合。
也就是说:闭包让开发者可以从内部函数访问外部函数的作用域。
在 JavaScript 中,闭包会随着函数的创建而被同时创建。
上面这3句话是mdn的解释。太复杂了。笨笨的我理解不了。

我的理解:闭包是函数跨作用域的一种体现,函数跨作用域访问变量,就会形成闭包。
        常见的是子函数访问父函数。
表现形式:内层函数访问引用外层函数中的变量

闭包的优点和缺点

优点:变量私有化,减少全局污染。
缺点:变量常驻内存,永久不被js垃圾回收机制回收。【可能】造成内存开销过大或泄漏。

下面就是一个简单的闭包

function demo1(){
  let a = 1
  function b1(){
    return a+1
  }
  b1()
}
demo1()

什么时候闭包需要return

什么情况下闭包需要 return ?当我们需要使用闭包中的某一个值
比如我们需要知道某个函数被调用了多少次?

闭包的应用:一个函数被调用了多少次

function count(){
  // 变量是被私有化的。外部的更改是不会对它造成影响
  let num = 0
  function useNum(){
    // 返回的是一个数值
    return ++ num
  }
  // 返回的是一个函数
  return useNum
}
let c = count()
// 因为返回的是一个函数,因我们需要再次使用括号进行调用
console.log(c()) // 1
console.log(c()) // 2
console.log(c()) // 3

01png

闭包的应用-节流

<body>
    <input type="text" >
    <input type="submit" id="btn"  value="点击我">
    <script>
      const btn = document.getElementById("btn");
      btn.addEventListener('click', throttleFn(submitHandler,2500),false)
      function submitHandler(){
        console.log('点击了')
      }
      function throttleFn(func, delay) {
        let begin = 0;
        return function () {
            let cur = new Date().getTime();
            if (cur-begin>delay) {
                func.apply(this, arguments)
                begin=cur
            }
        }
      }
    </script>
</body>

闭包会造成内存泄漏吗?

闭包会造成内存泄漏吗? 其实闭包不一定会造成内存泄漏。
要说内存泄漏,我们就需要知道什么是内存泄漏?
内存泄漏是指:用不到的变量依然占据着内存空间,不能够被再次利用。
如递归或者方法无法结束。
在这里我们可以得出:像 num 变量就可能存在内存泄漏。
因为用不到的变量没有依然占据着内存空间。
但并不是所有的闭包都会造成内存泄漏。
像react,vue里面就用到的闭包,但是我们不能去回收。

如何清除闭包中的占据着内存的变量

  function count(){
    // 变量是被私有化的。外部的更改是不会对它造成影响
    let num = 0
    function useNum(){
      // 返回的是一个数值
      return ++ num
    }
    // 返回的是一个函数
    return useNum
  }
  let c = count()
  // 因为返回的是一个函数,因我们需要再次使用括号进行调用
  console.log(c()) // 1
  // 斩断 c 与 count 的联系,
  // 此时浏览器的垃圾回收机制就能回收闭包中num占有的空间
  c = null

闭包返回一个函数[常见面试题]

let num  =10
function f1(){
let num = 0
function fn(){
  num++
  console.log('num的值', num)
}
// 这个时候你返回来的是一个函数
return fn
}
let ff = f1()
ff()
ff()

输出的值是: 先是1 然后是2

闭包-从函数定义的地方开始查找值

let a = 20
// 函数执行的地方
function say(){
  console.log(a)
}

function sum() {
  let a = 40
  // 函数调用的地方
  say()
}
sum() // 输出的是20

分析:先创建了一个全局变量 a =20
然后又创建了一个局部变量 a = 40
say这个函数是一个全局函数,它里面访问的应该是全局变量20;
总结:它会从函数定义的地方开始去查找值,而不是函数执行的地方开始查找值

闭包-从函数定义的地方开始查找值 [函数作为参数传递]

var a = 'hei'
// 函数定义的地方
function foo(){
  var a = 'ha'
  function fo(){
    console.log(a)
  }
  // return的是一个函数
  return fo
}

function fn(say){
  var a = 'fn'
  // 函数调用的地方
  say()
}
fn(foo())

分析:这个题其实和上一个题本质上是一样的。
它会从函数定义的地方开始去查找值,而不是函数执行的地方开始查找值。
function fn(say){
  var a = 'fn'
  // 函数调用的地方
  say()
}
这一段代码是迷惑我们的,我们只需要记住从函数定义的地方开始去查找就行

立即执行

var a = 1
function fn(){
  var a =2
  function f() {
    a++;
    console.log(a)
  }
  f() // 会被立即执行一次 
  return f // 返回的是一个函数
}
// fn() 的时候 会立即执行 f()
var x = fn()
x() 
// 输出3 然后输出4

立即执行 + 闭包

var n = 10
function fn(){
    var n =20
    function f() {
       n++;
       console.log(n)
     }
    f() // 21
    return f
}

var x = fn()
x() // 22
x() // 23
console.log(n) // 10