[Day 21] 闭包 (Closure)是什麽?

前言

闭包的观念,其实就和前几天谈到的作用域、变数宣告和作用域的观念有关。只要有清楚知道其中差异,在理解闭包上就也能够透过范例轻松理解了~。

学习资源分享

  1. JavaScript - 十分钟带你了解 闭包 (Closure),主要是透过六角学院的这个影片来理解,而今天文章范例也是使用该影片中的范例来说明。
  2. 深入浅出了解 JavaScript 闭包(closure),PJCHENder的文章,也很清楚的用范例一步步的说明没有使用闭包会发生什麽情形,闭包的应用方式为何。

范例解说

呼叫函式内的函式

我们主要是要认识作用域,理解变数所能够被存取的范围,所以范例中,我们要去注意变数money

  • 在第一层的函式内,宣告变数 money=1000
  • 内层,再撰写一个function,是执行money - price ,而他能向外取用到 money = 1000的值
  • 在 myMoney 中,会取得 1000,并带入参数为 price=100 ,执行 1000-100
  • console 出的结果为 900,再去callMethod(),里面传入参数 => 会随参数的数值去扣除
function callMethod() {
  var money = 1000;
  return function (price) {    
    money = money - price;
    return money;
  }
}
let myMoney = callMethod()(100);  // 存取内部函式的变数
console.log(myMoney);

补充说明

为何要宣告变数 let myMoney = callMethod()(100);
若直接呼叫callMethod(),都是回传函式

所以透过宣告新的变数,将callMethod()中的function存到变数 myMoney 中,并让该变数可以执行。

增加全域变数 changeMoney

  • 注意changeMoney的宣告,没有使用var或let,此方式的宣告就会直接为全域变数
    • 如果直接执行changeMoney的话,会使 money 直接为 500
  • 藉此范例,可以看到在闭包中,就是让宣告的变数存在执行的范围(函式)内
function callMethod() {
  var money = 1000;
  changeMoney = function () {
    money = 500;
  }
  return function (price) {       // 这里就是一个闭包,不过目前只会使用一次
    money = money - price;
    return money;
  }
}
let updateMyMoney = callMethod();  // 存取内部函式的变数
console.log(updateMyMoney(100));
  • 在呼叫changeMoney之前,我们随着带入参数的数值,会去执行money - price
  • 执行changeMoney()之後,money = 500,带入参数後,也会从500去扣

闭包的用意

让一个函式可以存取多个变数,并且能够分别执行函式中的任务

  • 外层callMethod的函式,带入newMoney的参数
  • 最後宣告两个新变数 MyMoney YourMoney,并传入不同数值

function callMethod(newMoney) {
  var money = newMoney || 1000;
  return function (price) {
    money = money - price;
    return money;
  }
}
let MyMoney = callMethod(500);
let YourMoney = callMethod(1000);
console.log(MyMoney(100));
console.log(YourMoney(100));

记得善用 let,产生区块作用域

上述有提及因变数在全域环境的关系,所以在呼叫changeMoney时,要取得的值会被覆盖。
在 ES6 透过 let 它可以帮我们把所定义的变数缩限在 block scoped 中,简单说就是{}来区隔。

小结

自己的学习习惯,会透过影片课程中老师的解说,而自己边做笔记、或是跟着范例进行操作,来加深自己的记忆,并透过反覆思索能够更加理解内容。此外即便看过影片,也做完笔记,有时还会去搜寻 MDN 或是其他部落格文章,看看其他人用什麽角度来说明观念。


<<:  Day7-在认识 useMemo 前,先认识 React.memo

>>:  Day 06 - MVC 与三层架构

[D13] 空间滤波例子:Gaussian Filter

接者用高斯滤波器为例子,接者会对其权重参数(weighting, coefficient)讨论~ 所...

【Day 25】指标介绍(中)

昨天,我们介绍了指标的一些小概念,不知道读者们是否有比较清楚指标是甚麽东西呢?(我自己在学的时候,会...

【把玩Azure DevOps】Day26 YAML格式以外的Pipeline传统编辑器(Classic Editor)

先前在「CI/CD从这里:设定第一个Pipeline(范本与编辑介面介绍)」这篇文章内容建立Pipe...

伸缩自如的Flask [day 27] Supervisor

像gunicorn 及docker 有着执行时timeout的防止错误发生的机制, 但是要是超过了 ...

自动化测试,让你上班拥有一杯咖啡的时间 | Day 13 - 动态跳过测试用例

此系列文章会同步发文到个人部落格,有兴趣的读者可以前往观看喔。 测试脚本中有许多测试用例时,当需要...