Chapter 4
Dynamic objects
{}
4.1 Syntax
brandSpankingNewObject = {}
anotherOne = {}
{title: 'Way of the Dragon'}
{title: 'Way of the Dragon', star: 'Bruce Lee'}
{title: 'Way of the Dragon', actors: ['Bruce Lee', 'Chuck Norris']}
{title: 'Way of the Dragon', info: {budget: '$130000', released: '1972'}}
movie = {title: 'Way of the Dragon'}
{title: 'Enter the Dragon', info: {budget: '$850000', released: '1973'}}
title: 'Enter the Dragon'
info:
budget: '$850000'
released: '1973'
Listing 4.1 Comparison of YAML literal with brace literal notation
YAML object literals
futurama =
characters: [
'Fry'
'Leela'
'Bender'
'The Professor'
'Scruffy'
]
quotes: [
'Good news everyone!'
'Bite my shiny metal'
]
|
Brace object literals
futurama = {
characters: [
'Fry',
'Leela',
'Bender',
'The Professor',
'Scruffy'
],
quotes: [
'Good news everyone!'
'Bite my shiny metal'
]
}
|
4.2 Key-value stores
phoneNumbers =
hannibal: '555-5551'
darth: '555-5552'
hal9000: 'disconnected'
freddy: '555-5554'
'T-800': '555-5555'
phoneNumbers.darth
# '555-5552'
phoneNumbers.hal9000
# 'disconnected'
phoneNumbers.T-800
# NaN
phoneNumbers.T - 800
phoneNumbers['T-800']
# '555-5555'
phoneNumbers['freddy']
# '555-5554'
friendToCall = 'hannibal'
phoneNumbers[friendToCall]
# '555-5551'
friendToCall = 'darth'
phoneNumbers[friendToCall]
# '555-5552'
phoneNumbers.kevin = '555-5556'
phoneNumbers['Agent Smith'] = '555-5557'
phoneNumbers =
hannibal: '555-5551'
darth: '555-5552'
hal9000: 'disconnected'
freddy: '555-5554'
'T-800': '555-5555'
'kevin': '555-5556'
'Agent Smith': '555-5557'
phoneNumbers.hannibal = '555-5525'
phoneNumbers.hannibal
# '555-55525'
'hal9000' of phoneNumbers
# true
'skeletor' of phoneNumbers
# false
Lisning 4.2 A simple phone book
phonebook =
numbers:
hannibal: '555-5551'
darth: '555-5552'
hal9000: 'disconnected'
freddy: '555-5554'
'T-800': '555-5555'
list: ->
"#{name}: #{number}" for name, number of @numbers
add: (name, number) ->
if not (name of @numbers)
@numbers[name] = number
get: (name) ->
if name of @numbers
"#{name}: #{@numbers[name]}"
else
"#{name} not found"
console.log "Phonebook. Commands are add get list and exit."
process.stdin.setEncoding 'utf8'
stdin = process.openStdin()
stdin.on 'data', (chunk) ->
args = chunk.split(' ')
command = args[0].trim()
name = args[1].trim() if args[1]
number = args[2].trim() if args[2]
switch command
when 'add'
res = phonebook.add(name, number) if name and number
console.log res
when 'get'
console.log phonebook.get(name) if name
when 'list'
console.log phonebook.list()
when 'exit'
process.exit 1
> coffee phonebook.coffee
Phonebook. Commands are add, get, list and exit.
hannibal: 555-5551
darth: 555-5552
hal9000: disconnected
freddy: 555-5554
T-800: 555-5555
> get freddy
freddy: 555-5554
> add kevin 555-5556
kevin: 555-556
orderMaserati = (color) ->
"""Order summary
- Make: Gran Turismo S
- Color: #{color}
"""
orderMaserati 'Nero Carbonio'
# Order summary:
# - Make: Gran Turismo S
# - Color: Nero Carbonio
orderMaserati = (exteriorColor, interiorColor) ->
"""Order summary:
- Make: Gran Turismo
Options:
- Exterior color: #{exteriorColor}
- Interior color: #{interiorColor}
"""
orderMaserati = (exteriorColor, interiorColor, wheelRims) ->
"""Order summary:
Make: Gran Turismo
Options:
Exterior color: #{exteriorColor}
Interior color: #{interiorColor}
Wheel rims: #{options.wheelRims}
"""
orderMaserati = (options) ->
"""Order summary:
Make: Gran Turismo
Options:
Exterior color: #{options.exterior}
Interior color: #{options.interior}
Interior trim: #{options.trim}
Wheel rims: #{options.rims}
"""
orderMaserati exterior:'red', interior:'red', trim:'walnut', wheels:'18'
Order summary:
Make: Gran Turismo
Options:
Exterior color: red
Interior color: red
Interior trim: walnut
Wheel rims: 18
element = {}
styles =
width: '10px'
color: 'red'
css element, styles
element.style.width
# '10px'
element.style.color
# 'red'
4.3 Comprehensions
views =
'ren': 30
'stimpy': 10
views.ren = views.ren + 1
number + 1 for number in [1,2,3,4,5]
# [2,3,4,5,6]
expression for property of object
Listing 4.3 Comprehension compared to for...in loop
CoffeeScript
movie =
title: 'From Dusk till Dawn'
released: '1996'
director: 'Robert Rodriguez'
writer: 'Quentin Tarantino'
for property of movie
console.log property
|
JavaScript
var movie = {
title: 'From Dusk till Dawn',
released: '1996',
director: 'Robert Rodriguez',
writer: 'Quentin Tarantino'
}
for (var property in movie) {
console.log(property);
}
|
Listing 4.4 Comprehension as expression
CoffeeScript
properties = (prop for prop of movie)
|
JavaScript
var properties = [];
for (var prop in movie) {
properties.push(prop);
}
|
name for name of {bob: 152, john: 139, tracy: 209}
# ['bob', 'john', 'tracy']
views =
'/reviews/pool-of-radiance': 121
'/reviews/summer-games': 90
'/reviews/wasteland': 139
'/reviews/impossible-mission': 76
page for page of views
[ '/reviews/pool-of-radiance'
'/reviews/summer-games'
'/reviews/wasteland'
'/reviews/impossible-mission' ]
value for property, value of object
score for name, score of {bob: 152, john: 139, tracy: 209}
# [152, 139, 209]
count for page, count of views
[ 121, 90, 139, 76 ]
expression for property, value of object
for property, value of object
expression
sum = 0
for page, count of views
sum = sum + count
# [121, 211, 350, 426]
Listing 4.5 Page views
views = {}
viewsIncrement = (key) ->
views[key] ?= 0
views[key] = views[key] + 1
total = ->
sum = 0
for own page, count of views
sum = sum + count
sum
if !views['donatello']?
views['donatello'] = 1
views['donatello'] ?= 0
views[key] =? 0
for own url, count of views
4.4 Structured data
Listing 4.6 JSON status updates
response = {
"results": {
"23446": {
"user": "Guard",
"text": "Found them? In Mercia?! The coconut's tropical!"
},
"23445": {
"user": "Arthur",
"text": "We found them."
},
"23443": {
"user": "Guard",
"text": "Where'd you get the coconuts?"
}
}
}
response.results
# { "23443":
# { user: "Guard",
# text: "Where\'d you get the coconuts?" },
# "23445":
# { user: "Arthur",
# text: "We found them." },
# "23446":
# { user: "Guard",
# text: "Found them? In Mercia?! The coconut\'s tropical!" }
# }
response.results['23443'].user
# "Guard"
response.results['23445'].text
# "We found them."
response.results['23446']
# {user: "Guard", text: "Found them? In Mercia?! The coconut\'s tropical!"}
4.5 Binding
yourObject =
someFunction: ->
yourObject.someFunction()
<!doctype html>
<title>How many people are coming?</title>
<body>
<div id='#some-element'>Change this text</div>
someElement = document.querySelector('#some-element')
someElement.onclick = ->
someElement.innerHTML 'Got clicked!'
<!doctype html>
<title>How many people are coming?</title>
<body>
<div id='#some-element'>Change this text</div>
<div id='#some-other-element'>Change this text</div>
someElement = document.querySelector('#some-element')
someOtherElement = document.querySelector('#some-other-element')
someElement.onclick = ->
someElement.innerHTML = 'Got clicked!'
someOtherElement.onclick = ->
someOtherElement.innerHTML = 'Got clicked!'
someElement.onclick = ->
someElement.innerHTML = 'Got clicked!'
someElement.onclick = ->
this.innerHTML = 'Got clicked!'
someElement.onclick = ->
@innerHTML = 'Got clicked!'
@.innerHTML 'Got clicked!'
markAsClicked = ->
@innerHTML = 'Got clicked!'
someElement.onclick = markAsClicked
someOtherElement.onclick = markAsClicked
document.querySelector('#some-element').onclick = ->
someElement.onclick = ->
setTimeout ->
@innerHTML 'Got clicked'
, 1000
noumenon =
value: 'noumenon',
itself: -> @
noumenon.itself().value
# 'noumenon'
detachedFromObject = noumenon.itself
detachedFromObject()
# 'undefined'
other =
value: 'other'
other.itself = noumenon.itself
other.itself().value
# 'other'
noumenon.itself = -> console.log @value
setTimeout noumenon.itself, 1000
# 'undefined'
someElement.onclick = ->
clickedElement = @
setTimeout ->
clickedElement.innerHTML 'Got clicked'
, 1000
someElement.onclick = ->
setTimeout =>
this.innerHTML 'Got clicked'
, 1000
4.6 Prototypes
cassette =
title: "Awesome songs. To the max!"
duration: "10:34"
released: "1988"
track1: "Safety Dance - Men Without Hats"
track2: "Funkytown - Lipps, Inc"
track3: "Electric Avenue - Eddy Grant"
track4: "We Built This City - Starship"
cassetteCopy = {}
for own property, value of cassette
cassetteCopy[property] = value
cassetteCopy.track3
# "Electric Avenue ? Eddy Grant"
cassette.track5 = "Rock Me Amadeus - Falco"
cassetteCopy.track5?
# false
musicDevice = Object.create cassette
cassette.track6 = "Sledgehammer ? Peter Gabriel"
musicDevice.track6 is "Sledgehammer ? Peter Gabriel"
# true
musicDevice.track1 = "Toy Soldiers - Markita"
musicDevice.track1 is "Toy Soldiers - Markita"
# true
cassette.track1 is "Safety Dance - Men Without Hats"
# true
musicDevice.track7 = " Buffalo Stance - Neneh Cherry"
cassette.track7?
# false
4.7 Behavior
businessViews = {}
pleasureViews = {}
increment = (recipient, key) ->
recipient[key] ?= 0
recipient[key] = recipient[key] + 1
total = (recipient) ->
sum = 0
for own page of recipient
sum = sum + 1
sum
increment businessViews, '/products/fade-o-meter/enterprise-edition'
Listing 4.7 Page views revisited
views =
clear: ->
@pages = {}
increment: (key) ->
@pages ?= {}
@pages[key] ?= 0
@pages[key] = @pages[key] + 1
total: ->
sum = 0
for own page, count of @pages
sum = sum + count
sum
businessViews = Object.create views
personalViews = Object.create views
for i in [1..100] by 1
businessViews.increment '/product-details/2454'
businessViews.total()
# 100
personalViews.total()
# 0
views.pagesTotal = ->
(url for own url of @).length
businessViews.pagesTotal()
4.8 Classes
function Views () {
this.pages = {};
}
businessViews = new Views();
Listing 4.8 A page views class
class Views
constructor: ->
@pages = {}
increment: (key) ->
@pages[key] ?= 0
@pages[key] = @pages[key] + 1
total: () ->
sum = 0
for own url, count of @pages
sum = sum + count
sum
businessViews = new Views
personalViews = new Views
class Empty
empty = new Empty
newViewsObject = new Views
businessViews is personalViews
# false
objectReferenceOne = {}
objectReferenceTwo = objectReferenceOne
objectReferenceTwo is objectReferenceOne
# true
class Black
green = new Black
green is new Black
# false
businessViews is personalViews
# false
4.9 Putting it together
> coffee 4.9.coffee
Listing 4.9 Page views web application
http = require 'http'
class Views
constructor: ->
@pages = {}
increment: (key) ->
@pages[key] ?= 0
@pages[key] = @pages[key] + 1
total: ->
sum = 0
for own url, count of @pages
sum = sum + count
sum
businessViews = new Views
personalViews = new Views
server = http.createServer (request, response) ->
renderHit = (against) ->
against.increment request.url
response.writeHead 200, 'Content-Type': 'text/html'
response.end "recorded"
if request.url is '/'
response.writeHead 200, 'Content-Type': 'text/html'
response.end """
Personal: #{personalViews.total()}
Business: #{businessViews.total()}
"""
else if /\/business\/.*/.test request.url
renderHit businessViews
else if /\/personal\/.*/.test request.url
renderHit personalViews
else
response.writeHead 404, 'Content-Type': 'text/html'
response.end "404"
server.listen 8080, '127.0.0.1'
4.10 Summary