From the wiki article, a closure (computer science) is a function or reference to a function together with a referencing environment-- a table storing a reference to each of the non-local variables of that function.
Closure-like constructs include callbacks and as such, are important in asynchronous programming. Here is a simple example in PHP that uses a closure as a callback to compute the total price of a shopping cart by defining a reference table for the callback function and including variables tax and total:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// SOURCE: http://php.net/manual/en/functions.anonymous.php | |
// A basic shopping cart which contains a list of added products | |
// and the quantity of each product. Includes a method which | |
// calculates the total price of the items in the cart using a | |
// closure as a callback. | |
class Cart | |
{ | |
const PRICE_BUTTER = 1.00; | |
const PRICE_MILK = 3.00; | |
const PRICE_EGGS = 6.95; | |
protected $products = array(); | |
public function add($product, $quantity) | |
{ | |
$this->products[$product] = $quantity; | |
} | |
public function getQuantity($product) | |
{ | |
return isset($this->products[$product]) ? $this->products[$product] : | |
FALSE; | |
} | |
public function getTotal($tax) | |
{ | |
$total = 0.00; | |
$callback = | |
function ($quantity, $product) use ($tax, &$total) | |
{ | |
$pricePerItem = constant(__CLASS__ . "::PRICE_" . | |
strtoupper($product)); | |
$total += ($pricePerItem * $quantity) * ($tax + 1.0); | |
}; | |
array_walk($this->products, $callback); | |
return round($total, 2); | |
} | |
} | |
$my_cart = new Cart; | |
// Add some items to the cart | |
$my_cart->add('butter', 1); | |
$my_cart->add('milk', 3); | |
$my_cart->add('eggs', 6); | |
// Print the total with a 5% sales tax. | |
print $my_cart->getTotal(0.05) . "\n"; | |
// The result is 54.29 | |
?> |
The concept of closures in javascript is important to understand because you might not even know you're using it. If you write in coffee-script classes or do classical inheritance patterns in vanilla javascript or even write callbacks in general for asynchronous programming, you are probably using closures. The following example is a starting point for classical inheritance in javascript. This shows how to hide private variables. It doesn't use "new" but the pattern is very similar.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function car() { | |
// Private Variable | |
var val = { | |
make : "generic", | |
model : "generic", | |
color : "generic" | |
}; | |
// "new" instance | |
return { | |
setMake: function(newMake) { | |
make = newMake || "generic"; | |
val.make = make; | |
}, | |
getMake: function() { | |
return val.make; | |
}, | |
setModel: function(newModel) { | |
model = newModel || "generic"; | |
val.model = model; | |
}, | |
getModel: function() { | |
return val.model; | |
}, | |
setColor: function(newColor) { | |
color = newColor || "generic"; | |
val.color = color; | |
}, | |
getColor: function() { | |
return val.color; | |
}, | |
self: function() { | |
return val; | |
} | |
} | |
} | |
// Create our instances | |
var juke1 = car(); | |
var juke2 = car(); | |
// Set make | |
juke1.setMake("nissan"); | |
juke2.setMake("nissan"); | |
// Set model | |
juke1.setModel("juke"); | |
juke2.setModel("juke"); | |
// Set color | |
juke1.setColor("red"); | |
juke2.setColor("black"); | |
// Cannot get private variable, cool! | |
juke1.val // undefined | |
juke2.val // undefined | |
// Use getter for access to private data | |
juke1.self() // Object {make: "nissan", model: "juke", color: "red"} | |
juke2.self() // Object {make: "nissan", model: "juke", color: "black"} |
According to Effective Javascript: 68 Specific Ways To Harness The Power of Javascript, there are three essential facts regarding closures:
- JavaScript allows you to refer to variables that were defined outside of the current function
- Functions can refer to variables defined in outer functions even after those outer functions have returned
- Closures can update values of outer variables
Knowing this, we can do some fun stuff in Node.JS with asynchronous programming. With closures, we can pull a document collection from a NoSQL database, manipulate the results, and push it to an array stored via closure in the parent scope.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require './nosql' | |
require 'async' | |
# new results array. this will be available via closure | |
results = [] | |
# Start the NoSQL connection | |
nosql.connect ()-> | |
# Get the collection pointer | |
collection = nosql.getCollection 'collection' | |
# Find the documents | |
collection.find {}, (documents)-> | |
# Create our iterator fn to be consumed by the async library | |
eachDocument = (doc, cb)-> | |
# Do something to doc; add/remove/modify variables. | |
# Push it to the results array that is in a | |
# parent scope and available via closure. | |
results.push doc | |
async.forEach documents, eachDocument, ()-> | |
# At this point, we have finished getting and dealing with documents. | |
# We can close the db and deal with the results. | |
nosql.close() | |
# If we were writing this in an express route, use res.send | |
res.send { success: true, results: results } | |
Hopefully you will use closures to your advantage, especially when developing in javascript, be it server side or client side or even in the database (Postgres with v8).
No comments:
Post a Comment