中阶魔法 - 闭包 Closure (二)

前情提要

艾草:「昨天教你原理,今天我们实际来实作这个术式吧!」

「好~~」

艾草:「来!发动前要念咒语,霹雳卡霹雳拉拉波波力那贝贝鲁多!」

「这也太羞耻了吧!真的要吗...霹雳...波波..⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄.」

艾草:「嗯,很好,很棒,大声一点!」

「等等,我刚刚看你发动不用念呀?」

艾草:「嘿嘿 A____A !」


闭包 Closure

上一篇我们介绍了闭包的原理後,这篇来介绍关於闭包的实作方式。

范例

情境题:单人计算魔力总量之增加

假设今天想要计算魔力总量,学习入门魔法总量(mana)增加 100 、中阶魔法总量(mana)增加 200 。

目前状态:

  • 初始魔力总量 100
  • 入门魔法学习了 2 堂
  • 中阶魔法学习了 1 堂
function addMana() {
  let myMana = 100;
  return function (mana) {
    myMana += mana;
    return myMana;
  }
}

//将 shannonMana 指向 addMana()
let shannonMana = addMana();

//透过 shannonMana() 执行内层函式
console.log(shannonMana(100));//200
console.log(shannonMana(100));//300
console.log(shannonMana(200));//500
  1. 定义 addMana 函式,并在函式内定义变数魔力总量初始值 myMana 为 100
  2. addMana 内回传一个内层函式,内层函式内会取用外层 myMana 变数,并加入参数为新增的魔力总量 mana
  3. 於全域定义变数 shannonMana 并使其指向 addMana() 函式
  4. 透过呼叫 shannonMana() 并传入参数执行 内层函式的魔力总量新增

像这样就算出了,目前魔力总量为 500 !


当然闭包能做到的不止如此,今天如果有多人都想算魔力总量,还可以这样做!

情境题:多人计算魔力总量之增加

因为今天有多人了,每人的魔力总量初始值不一,所以先在 addMana() 函式内新增参数 initialMana,让大家都可以传入魔力总量初始值。

function addMana(initialMana) {
  let myMana = initialMana;
  return function (mana) {
    myMana += mana;
    return myMana;
  }
}

首先帮第一位来宾 Vivian 计算魔力总量:

  • 初始魔力总量 250
  • 入门魔法学习了 1 堂
  • 中阶魔法学习了 2 堂
let vivianMana = addMana(250); //传入初始值 250
console.log(vivianMana(100));//350
console.log(vivianMana(200));//550
console.log(vivianMana(200));//750

透过这个方式就能计算出 Vivian 目前魔力总量为 750 。

此时来了第二位来宾 Ami ,来帮他计算魔力总量:

  • 初始魔力总量 800
  • 入门魔法学习了 1 堂
  • 中阶魔法学习了 1 堂
let amiMana = addMana(800); //传入初始值 800
console.log(amiMana(100));//900
console.log(amiMana(200));//1100

像这样就可以算出 Ami 魔力总量为 1100 。

观察上方两个案例,流程基本如下:

  1. 宣告变数并赋予变数 addMana() 函式,并於函式内依情境传入初始魔力总量 initialMana
  2. 透过呼叫该变数,并使用小括号 () 於内层函式传入参数新增的魔力总量 mana

可以发现,透过将外层函式赋予给不同变数,再透过变数去呼叫内层函式,能达成计算多人魔力总量的结果。


情境题:计算魔力总量之新增、减少

魔力总量除了新增之外,也可能会有减少的情况,例如好一阵子没碰到 JavaScript 魔法後,根本不知道自己当初写了啥等。

为了因应多种情境,可以这样改写程序码:

function calcMana(initialMana) {
  let myMana = initialMana;
  return {
    addMana: function (mana) {
      myMana += mana;
      return myMana;
    },
    minusMana: function (mana) {
      myMana -= mana;
      return myMana;
    }
  }
}
  1. 将回传函式改为回传物件
  2. 物件内新增两个函式:addMana()minusMana() 负责计算增加与减少。

如果今天想计算来宾 Ann 的魔力总量:

  • 初始魔力总量 500
  • 中阶魔法学习了 2 堂
  • 太久没碰学习损失魔力总量 100
let AnnMana = calcMana(500); //传入初始值 500
console.log(AnnMana.addMana(200));//700
console.log(AnnMana.addMana(200));//900
console.log(AnnMana.minusMana(100));//800

像这样透过变数呼叫内层函式後,使用点记法选取物件属性,并执行对应函式,就可以计算多种条件!


参考文献

JavaScript 核心篇(六角学院)
https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Closures
https://wcc723.github.io/javascript/2017/12/13/javascript-closure/


<<:  ETA screen (4)

>>:  Vue.js指令(v-model)绑定(DAY27)

AI ninja project [day 13] 回归

这应该也是学习深度学习时的基础课程, 不确定跟图像分类比,哪一个会先学到, 但是在接触深度学习框架时...

D17 第八周 前端基础串 API

这礼拜的课程进度: FE102 中场休息到结束 FE102 後半段笔记摘要 介绍网页储存资料的方式 ...

第廿九天:重新开始奋发的周三

已经旅游到几乎开始习惯颓废了,所以要开始奋发!所以一早(有点晚,应该叫一晚?)就去很多插座的咖啡店。...

Unity与Photon的新手相遇旅途 | Day12-血量制作

今天的内容为该如何制作血量,并且在攻击时或受到伤害时改变血量。 ...

浅谈数位学习历程消失的一二事

这次与教育部合作的资讯厂商因为工程师「手滑」把硬碟还原,导致全国高中职数位学习历程「消失」的灾情不断...