(Day14) 闭包 (Closure) 介绍

闭包算是在 JS 中常听到,却不容易使用的一个方法,更多状况是不小心用出来,~~因此出 bug ~~

闭包与记忆体

在介绍闭包之前,先来看看下面范例:

function randomString(length) {
      var result = '';
      var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
      var charactersLength = characters.length;
      for (var i = 0; i < length; i++) {
        result += characters.charAt(Math.floor(Math.random() * charactersLength));
      }
      return result;
    }

    function getData() {
      var demoData = [];
      for (let i = 0; i < 1000; i++) {
        demoData.push(randomString(1000))
      }
    }
    getData();

randomString 是一个会根据设定执行数次,产生乱码字串的方法,而这乱码字串会藉由 return result 回传,最後又在 push 至 demoData 变数上。

使用 chrome 无痕模式来观察 Memory ,可以发现在执行上述程序码时,记忆体使用了 1.3 MB

再来执行另外一段非常相近的程序码,来看看他的记忆体使用量:

function randomString(length) {
      var result = '';
      var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
      var charactersLength = characters.length;
      for (var i = 0; i < length; i++) {
        result += characters.charAt(Math.floor(Math.random() * charactersLength));
      }
      return result;
    }
    
    var demoData = [];
    function getData() {
      for (let i = 0; i < 1000; i++) {
        demoData.push(randomString(1000))
      }
    }
    getData();

Untitled

可以发现这个范例记忆体增使用量将近 21MB ,这是因为第二个范例 demoData 变数是被放在外层也就是全域( window)底下,而 demoData 变数此时是能在被呼叫、使用的。

而第一个范例中, demoData 是在函示里头, demoData 变数则会跟着 getData 执行完毕时,一同被释放记忆体,此时 demoData 也是无法被呼叫的,因此两者记忆体落差十分大。

之所以要先讲这一段是因为,记忆体可以说是闭包的一个重点。

闭包范例与解释

接下先来看看闭包的一个简单范例

function openFn() {
      let num = 10
      function ClosureFn(newNum) {
        num = num + newNum
        return num
      }
      //函示内 return 函示就会变成 『闭包』
      return ClosureFn
    }
const useClosure = openFn()

console.log(useClosure(10)) //20
console.log(useClosure(10)) //30
console.log(useClosure(10)) //40

在上面范例中 ClosureFn 其实就是闭包,若要执行这个闭包可以直接使用 openFn()(100) 其就会被执行,不过一般来说我们不会直接使用两个 ()() 小刮号做执行,而是像上面范例中使用 openFn() 并且再用一个变数来做指向。

而上面有提到记忆体是闭包的重点,关於这一点我们可以看看连续执行 useClosure() 後回传的值会不断叠加,然而 ClosureFn 闭包函示内部虽然没有 let num = 100 ,不过闭包内部会因为 num = num + newNum 这段程序码,有使用到 num 变数,因此按照作用域的规则,会访问(参考)外层函式的 let num 变数,因为这个访问(参考)的动作,就会让 num 变数的记忆体『不被释放』,因此当正是因为这个『不被释放』,我们使用 useClosure(10) 的值才可以不断被叠加。

这边也试者使用图片来增加对闭包的理解:

之所以要使用闭包,就是因为可以透过不同变数、常数,让闭包回传资料各自独立,某些需要重复使用程序码的状况就可以使用闭包,例如

function openFn() {
      let num = 10
      function ClosureFn(newNum) {
        num = num + newNum
        return num
      }
      //函示内 return 函示就会变成 『闭包』
      return ClosureFn
    }
const useClosure1 = openFn()
console.log(useClosure1(10))
console.log(useClosure1(10))
const useClosure2 = openFn()
console.log(useClosure2(100))
console.log(useClosure2(100))

下篇则会介绍闭包延伸运用。

参考文献


<<:  DAY14 挑选合适的模型进行训练

>>:  .NET Core第12天_服务依赖注入_IoC容器生命周期_ConfigureServices

Day16:今天来聊一下如何使用njRAT RAT Trojan控制Windows电脑攻防

攻击者会使用Trojan horses(木马程序)来诱骗使用者在电脑上执行预先设计的操作, 一旦木马...

【Day 02】战略资讯系统(Strategic information system)

战略资讯系统 中台架构其实是一种整体资讯架构订定的思维,其目的是为了迎接企业不断创新的挑战,在『变是...

简单说回归 | ML#Day14

wiki 说明页:回归分析 想了解详细的说明,请见wiki或者其他参考资料。 或者也可以直接看下面,...

卡夫卡的藏书阁【Book9】- Kafka Partition Reassign

“The meaning of life is that it stops.” ― Franz K...

Day 20 舍弃Storyboard并使用XIB来写app

虽然storyboard是个对初学者比较方便使用的东西,但是当你有很多元件要用,修改来讲的话就就会比...