http://wiki.spheredev.org/index.php?title=JavaScript/Higher-order_programming/Example_1&feed=atom&action=historyJavaScript/Higher-order programming/Example 1 - Revision history2024-03-28T16:04:28ZRevision history for this page on the wikiMediaWiki 1.29.0http://wiki.spheredev.org/index.php?title=JavaScript/Higher-order_programming/Example_1&diff=836&oldid=prevApollolux: created2013-06-15T00:43:36Z<p>created</p>
<p><b>New page</b></p><div>We have HP, and we want some way to heal ourselves:<br />
<syntaxhighlight><br />
var hero = {hp: 10, hpMax: 85};<br />
</syntaxhighlight><br />
<br />
It'd be nice to have a Potion, a Super Potion and a Mega Potion, healing 10, 20 and 50 HP respectively. It'd also be nice to see a dialog saying which item was used and how much HP we recovered.<br />
<br />
===The chump's way===<br />
<syntaxhighlight><br />
function Potion() {<br />
var oldHp = hero.hp;<br />
hero.hp += 10;<br />
if (hero.hp > hero.hpMax) hero.hp = hero.hpMax;<br />
<br />
var heal = hero.hp - oldHp;<br />
if (heal > 0)<br />
Print.printLine("Drank Potion! Recovered " + heal + " HP.");<br />
else<br />
Print.printLine("Drank Potion! Nothing happened.");<br />
}<br />
<br />
function SuperPotion() {<br />
var oldHp = hero.hp;<br />
hero.hp += 20;<br />
if (hero.hp > hero.hpMax) hero.hp = hero.hpMax;<br />
<br />
var heal = hero.hp - oldHp;<br />
if (heal > 0)<br />
Print.printLine("Drank Super Potion! Recovered " + heal + " HP.");<br />
else<br />
Print.printLine("Drank Super Potion! Nothing happened.");<br />
}<br />
<br />
function MegaPotion() {<br />
var oldHp = hero.hp;<br />
hero.hp += 50;<br />
if (hero.hp > hero.hpMax) hero.hp = hero.hpMax;<br />
<br />
var heal = hero.hp - oldHp;<br />
if (heal > 0)<br />
Print.printLine("Drank Mega Potion! Recovered " + heal + " HP.");<br />
else<br />
Print.printLine("Drank Mega Potion! Nothing happened.");<br />
}<br />
</syntaxhighlight><br />
<br />
Ugh. Copy-pasting this was a pain. Imagine if we had to change the message format, or if we had 10 items. Miserable. Absolutely miserable. Anyway, we can use it like this:<br />
<syntaxhighlight><br />
var inventory = [Potion, SuperPotion, MegaPotion];<br />
inventory[0]();<br />
inventory[1]();<br />
inventory[2]();<br />
</syntaxhighlight><br />
<br />
With the output:<br />
<pre><br />
Drank Potion! Recovered 10 HP.<br />
Drank Super Potion! Recovered 20 HP.<br />
Drank Mega Potion! Recovered 45 HP.<br />
</pre><br />
<br />
===A smarter way===<br />
These potions do almost exactly the same thing. Let's reflect that in our code. We express the idea of a healing item '''once and only once'''. In fact, there's no reason to restrict it to HP.<br />
<syntaxhighlight><br />
function HealingItem(name, stat, amount) {<br />
return function () {<br />
var oldStat = hero[stat];<br />
hero[stat] += amount;<br />
if (hero[stat] > hero[stat + "Max"]) hero[stat] = hero[stat + "Max"];<br />
<br />
var heal = hero[stat] - oldStat;<br />
if (heal > 0)<br />
Print.printLine("Drank " + name + "! Recovered " + heal + " " + stat.toUpperCase() + ".");<br />
else<br />
Print.printLine("Drank " + name + "! Nothing happened.");<br />
};<br />
}<br />
<br />
function Potion() {<br />
return HealingItem("Potion", "hp", 10);<br />
}<br />
<br />
function SuperPotion() {<br />
return HealingItem("Super Potion", "hp", 20);<br />
}<br />
<br />
function MegaPotion() {<br />
return HealingItem("Mega Potion", "hp", 50);<br />
}<br />
</syntaxhighlight><br />
<br />
Here, the higher-order function is <code>HealingItem()</code>, because, as you can see, it returns a function as its result. Note that even after <code>HealingItem()</code> has returned, <var>name</var>, <var>stat</var> and <var>amount</var> are still "remembered". This funky property of returned functions is known as '''closure'''. We won't cover that here, but suffice to say, they're useful.<br />
Potion(), SuperPotion() and MegaPotion() just wrap conveniently around HealingItem() so we don't have to provide all the mundane parameters such as the name, statistic and amount each time. We call them wrappers: they make code easier to read.<br />
<br />
We can use them as before:<br />
<syntaxhighlight><br />
var inventory = [Potion(), SuperPotion(), MegaPotion()];<br />
inventory[0]();<br />
inventory[1]();<br />
inventory[2]();<br />
</syntaxhighlight><br />
<br />
Producing the same output:<br />
<pre><br />
Drank Potion! Recovered 10 HP.<br />
Drank Super Potion! Recovered 20 HP.<br />
Drank Mega Potion! Recovered 45 HP.<br />
</pre><br />
<br />
Note that <code>Potion()</code>, <code>SuperPotion()</code> and <code>MegaPotion()</code> now ''make'' items, so we need to put the parentheses there to "manufacture" the potions.<br />
<br />
This demonstrates both the power of JavaScript's functions, but also the flexibility provided by its '''reflection''': seeing and using program info when the program is running. Thanks to not restricting ourselves to HP, we can make ethers too:<br />
<syntaxhighlight><br />
function Ether() {<br />
return HealingItem("Ether", "mp", 10);<br />
}<br />
<br />
function SuperEther() {<br />
return HealingItem("Super Ether", "mp", 20);<br />
}<br />
<br />
function MegaEther() {<br />
return HealingItem("Mega Ether", "mp", 50);<br />
}<br />
</syntaxhighlight><br />
<br />
In the chump's way, this would have doubled the size of the code.</div>Apollolux