Dynamic objects - Foundations - CoffeeScript in Action (2014)

CoffeeScript in Action (2014)

Part 1. Foundations

Chapter 4. Dynamic objects

This chapter covers

· Objects as data

· Object comprehensions

· An introduction to prototypes

· An introduction to classes

JavaScript objects are simple and powerful, but because they don’t work quite like objects in other popular programming languages, they’re often wrongly perceived as confusing and underpowered. With simplified and familiar syntax, CoffeeScript both eliminates the confusion and better exposes the inherent power of objects. Before diving in, though, what exactly is an object?

Objects are collections of named properties where each name maps to a value. The value of a property can be any valid CoffeeScript value. You write an empty object with a set of curly braces:

{}

That is an object, in literal notation. The literal notation for objects is small and convenient. It’s used in the source code of programs to represent data and as a format for transferring data on the web.

Beyond being simple property containers, objects are also used for managing state and structuring programs. Objects in CoffeeScript are based on a concept called prototypes, which have the advantages of being dynamic and flexible but the disadvantages of being uncommon in other programming languages and unfamiliar to most programmers. It’s classes, and not prototypes, that are a familiar concept in other programming languages, so CoffeeScript provides class syntax that allows programs to be structured using more familiar class-based techniques.

In this chapter you’ll learn the syntax for defining objects, how object properties and values work as key-value stores, how to write comprehensions for objects, how to structure data using objects, how prototypes work, how functions are bound to objects, and finally how common behavior among many objects is efficiently achieved with CoffeeScript’s class syntax.

4.1. Syntax

CoffeeScript has some types that you’ve encountered already. They are numbers, strings, booleans, functions, null, and undefined. Everything else is an object. Objects with properties and values are declared using either the literal brace notation familiar to JavaScript developers or a minimalist CoffeeScript syntax, described in this section.

4.1.1. Literals

One way to get a brand-new empty object, assigned to a variable, is with an object literal:

brandSpankingNewObject = {}

anotherOne = {}

To impress your friends, immediately start referring to this as ex nihilo object creation. Ex nihilo is a Latin term that means “out of nothing.” An object created using the literal notation isn’t created from something else—it’s just there, out of nothing.

4.1.2. Properties

Objects contain properties. Each property has a name and associated value. An object with one property title and value 'Way of the Dragon' looks like this:

{title: 'Way of the Dragon'}

A colon is used to separate the property name and value. When an object has more than one property, commas separate subsequent properties:

{title: 'Way of the Dragon', star: 'Bruce Lee'}

There is no restriction on the types of values that can be object properties. For example, a property can be an array:

{title: 'Way of the Dragon', actors: ['Bruce Lee', 'Chuck Norris']}

Or it can be another object. Here, the info property contains an object with properties named budget and released:

{title: 'Way of the Dragon', info: {budget: '$130000', released: '1972'}}

Because an object is a value, it can be assigned to a variable:

movie = {title: 'Way of the Dragon'}

4.1.3. YAML-style syntax

YAML (rhymes with camel) is a data format that uses significant whitespace, making it a natural fit for CoffeeScript. Objects are written either in the JavaScript style with curly braces and commas or with a syntax similar to YAML. Consider this valid CoffeeScript object:

{title: 'Enter the Dragon', info: {budget: '$850000', released: '1973'}}

It can also be expressed in the new YAML-style syntax:

title: 'Enter the Dragon'

info:

budget: '$850000'

released: '1973'

Either is acceptable, though the YAML-style syntax is more commonly used inside CoffeeScript programs, with the curly brace style often used when transferring data. The following listing shows a syntax comparison of two object literals related to Scruffy’s favorite television show,Futurama.

Listing 4.1. Comparison of YAML literal with brace literal notation

YAML object literals

Brace object literals

futurama =

characters: [

'Fry'

'Leela'

'Bender'

'The Professor'

'Scruffy'

]

quotes: [

'Good news everyone!'

'Bite my shiny metal'

]

Both sides declare a variable with the name futurama that contains two properties named characters and quotes. The value of the characters property is an array containing five strings and the quotes property is an array with two strings.

futurama = {

characters: [

'Fry',

'Leela',

'Bender',

'The Professor',

'Scruffy'

],

quotes: [

'Good news everyone!'

'Bite my shiny metal'

]

}

That covers what objects look like; how about what they’re used for? To start, the convenient literal notation for objects in CoffeeScript is useful for representing data such as key-value stores.

4.2. Key-value stores

A key-value store associates a set of keys with a set of values, each key being associated with one value. In CoffeeScript, objects are often used as key-value stores, similar to how a hash is used in Ruby, a hash table is used in Java, or a dictionary is used in Python.

Objects in CoffeeScript aren’t hash tables in the strict sense of the word, but they are effective as key-value stores. In particular, the convenient object literal syntax and lack of ceremony make them very effective key-value stores. In this section you’ll see how objects as key-value stores are used as data in a program and to name function arguments.

4.2.1. Data

Imagine you have phone numbers of your friends written on a piece of paper (figure 4.1). You want to write a program that stores these numbers and allows you to add new numbers, change existing numbers, and check whether you have the number for a particular person. How do you express what you see on the paper in CoffeeScript?

Figure 4.1. Phone numbers

Express the list of phone numbers as an object with person names as the property names and phone numbers as the values. The object that you express as a key-value store looks similar to the paper version. Here it’s assigned to a variable:

Now that you have the object in your program, how do you use it?

Accessing Properties

Use dot notation on the object with the property name to get the corresponding value. For example, to call Darth, get his phone number by using the key darth on the object referenced by the phoneNumbers variable:

phoneNumbers.darth

# '555-5552'

Calling Hal to ask him if he’s feeling better is similar:

phoneNumbers.hal9000

# 'disconnected'

This dot notation doesn’t always work, though. Some properties don’t play nice.

Quoted property names and square brackets

How do you get the phone number for T-800? This doesn’t work:

phoneNumbers.T-800

# NaN

Why doesn’t it work? Look at it again:

phoneNumbers.T - 800

You can’t use the minus symbol to access a property name because minus means subtraction. Instead, to use a property that’s not a valid name, you put quotes around the property and square brackets around the quotes:

phoneNumbers['T-800']

# '555-5555'

Although properties that aren’t valid names can’t be accessed with dot notation, any property can be accessed using square brackets:

phoneNumbers['freddy']

# '555-5554'

In general, dot notation is easier to read, so it should be preferred for accessing any properties that don’t contain special characters; that is, unless the property name is in a variable.

Property names from variables

Because the property name inside the square brackets is a string, it can be provided by a variable:

friendToCall = 'hannibal'

phoneNumbers[friendToCall]

# '555-5551'

friendToCall = 'darth'

phoneNumbers[friendToCall]

# '555-5552'

The name of the key can be generated dynamically. If you aren’t familiar with dynamic languages, leave that idea to settle for a little while before continuing.

Adding a property

By default, any objects you create are open, meaning they can be changed. Add a new phone number to your list by assignment:

phoneNumbers.kevin = '555-5556'

phoneNumbers['Agent Smith'] = '555-5557'

Now the object has those new friends in it, as if it were written like this:

phoneNumbers =

hannibal: '555-5551'

darth: '555-5552'

hal9000: 'disconnected'

freddy: '555-5554'

'T-800': '555-5555'

'kevin': '555-5556'

'Agent Smith': '555-5557'

Be careful because, although the numbers that were just added are shown at the end of the object, there’s no requirement that object properties be in any particular order. Always consider object properties to be unordered.

Changing a property

Properties can also be changed:

phoneNumbers.hannibal = '555-5525'

phoneNumbers.hannibal

# '555-55525'

Suppose you don’t know what properties are in an object. You don’t want to add to or change an object without knowing what properties it has.

Checking for a property name

If you want to check if an object contains a property, use the of operator:

'hal9000' of phoneNumbers

# true

'skeletor' of phoneNumbers

# false

That covers basic object creation and manipulation. In listing 4.2 you see these object features together in a working phone book application. This listing uses some techniques you haven’t seen yet, but those techniques are explained later in this chapter. The program, which runs on Node.js, doesn’t persist changes to the phone book; each time you run it, the phone book will be reset.

Listing 4.2. A simple phone book

What does listing 4.2 do when it runs? Try it on the REPL. When you start the program, it will wait for input:

> coffee phonebook.coffee

Phonebook. Commands are add, get, list and exit.

Now, list all the numbers by typing list and pressing Enter. You’ll see the following:

hannibal: 555-5551

darth: 555-5552

hal9000: disconnected

freddy: 555-5554

T-800: 555-5555

To get a specific number, type get followed by the name you want and press Enter:

> get freddy

freddy: 555-5554

To add a new number or change an existing one, enter add followed by the name and then the number. That number will be added or changed and the new value logged to the console:

> add kevin 555-5556

kevin: 555-556

Of course, the key-value feature of CoffeeScript objects isn’t just for creating phone books.

4.2.2. Key-values for named arguments

Imagine you’re purchasing a Maserati (to keep your Ferrari from getting lonely) and that there are different options available to you for customizing it. Suppose that the order process is represented as a single function that passes an argument with your requested color:

The orderMaserati function takes a single argument color and returns a string using a whitespace-preserving heredoc with the color inserted into the string via string interpolation (see section 2.5.2). Scruffy likes metallic black, or Nero Carbonio as Maserati calls it, so he orders that:

orderMaserati 'Nero Carbonio'

# Order summary:

# - Make: Gran Turismo S

# - Color: Nero Carbonio

Maserati lets you customize more than just the color, though. How will the order function handle other options?

Multiple arguments

Suppose the interior color is also an option; the function definition gets more complicated:

orderMaserati = (exteriorColor, interiorColor) ->

"""Order summary:

- Make: Gran Turismo

Options:

- Exterior color: #{exteriorColor}

- Interior color: #{interiorColor}

"""

As the number of options and hence arguments grows, this will get unwieldy very quickly. How many arguments are too many?

Too many arguments

Perhaps the second argument doesn’t seem so bad. How about a third?

orderMaserati = (exteriorColor, interiorColor, wheelRims) ->

"""Order summary:

Make: Gran Turismo

Options:

Exterior color: #{exteriorColor}

Interior color: #{interiorColor}

Wheel rims: #{options.wheelRims}

"""

The next time you use this function, you’ll find yourself wondering, was it the interior or exterior color that went first? That’s no way to live. Instead, you need a way to pass any number of arguments into a function and not have to remember the order. You do this with an object.

An options argument

Instead of having to pass each argument to the function in a particular order, use an object as a key-value store to pass all the options as one argument:

orderMaserati = (options) ->

"""Order summary:

Make: Gran Turismo

Options:

Exterior color: #{options.exterior}

Interior color: #{options.interior}

Interior trim: #{options.trim}

Wheel rims: #{options.rims}

"""

Try to invoke this version of the function on the REPL:

orderMaserati exterior:'red', interior:'red', trim:'walnut', wheels:'18'

Your REPL might not show the line breaks and whitespace correctly, but the output is as follows:

Order summary:

Make: Gran Turismo

Options:

Exterior color: red

Interior color: red

Interior trim: walnut

Wheel rims: 18

Using an object as an options argument saves you from having to remember too many things when several configuration parameters have to be passed to a function. When you find yourself adding another similar argument for the third time, you should consider using an object. An object used this way for arguments is similar to named arguments in other programming languages. Actually, with CoffeeScript you can make it look even better, but that’s a lesson saved for chapter 7.

Three strikes refactor

Doing the same thing in your program three times should be your pain threshold for repetition before you use or create a new abstraction to avoid the duplication.

4.2.3. Exercises

Spend some time exploring objects as key-value stores by completing the following:

· Change listing 4.2 to add an edit command that allows existing phone book entries to be changed.

· Write a function that uses an options argument containing CSS property names and values and sets them as style properties on an object. Invoking the function should look like this:

· element = {}

· styles =

· width: '10px'

· color: 'red'

· css element, styles

· element.style.width

· # '10px'

· element.style.color

# 'red'

With objects as key-value stores providing such a convenient way for you to put data in your CoffeeScript, you’d expect equally convenient ways to actually do things with that data. There are; one of them is called comprehensions.

4.3. Comprehensions

When you first encountered comprehensions (see section 2.6.4), they were used for arrays. Objects have comprehensions too, and they’re very useful. Imagine you have a website with two pages. You want to track how many views each page on your website gets and the total number of views for all pages. Your website pages live on real URLs, but for the sake of simplicity here, suppose they’re just called ren and stimpy. Now, if the ren page has so far received 30 views and the stimpy page 10 views, that can be represented with an object literal as follows:

views =

'ren': 30

'stimpy': 10

As you’ve seen, storing a property on the object is done with assignment. Use this to increment the count:

views.ren = views.ren + 1

What you want to do is add up the existing views. You do that with a comprehension. In this section you’ll learn how to use comprehensions to transform the properties and values of an object into other properties and values.

4.3.1. Object comprehensions

You saw previously that comprehensions allow you to deal with the elements of an array without having to write a bunch of boilerplate and iterate manually through every single thing in the array one by one, again and again and again. Here’s a refresher in case you forgot:

number + 1 for number in [1,2,3,4,5]

# [2,3,4,5,6]

Object comprehensions in CoffeeScript work similarly to how array comprehensions work. They have a basic format similar to array comprehensions, except they use the word of:

expression for property of object

What does an object comprehension do? The following listing is a side-by-side comparison with the conceptually equivalent (but not compiled) JavaScript for...in loop.

Listing 4.3. Comprehension compared to for...in loop

CoffeeScript

JavaScript

movie =

title: 'From Dusk till Dawn'

released: '1996'

director: 'Robert Rodriguez'

writer: 'Quentin Tarantino'

for property of movie

console.log property

var movie = {

title: 'From Dusk till Dawn',

released: '1996',

director: 'Robert Rodriguez',

writer: 'Quentin Tarantino'

}

for (var property in movie) {

console.log(property);

}

In the basic case of listing 4.3 the JavaScript version is fine. How about getting an array of property names, though? Compare that in the next listing.

Listing 4.4. Comprehension as expression

CoffeeScript

JavaScript

properties = (prop for prop of movie)

var properties = [];

for (var prop in movie) {

properties.push(prop);

}

The JavaScript version uses multiple statements and has to micromanage the state of the variables properties and prop. The CoffeeScript version does not.

Comprehending properties

The property names of an object are returned as an array using a comprehension:

name for name of {bob: 152, john: 139, tracy: 209}

# ['bob', 'john', 'tracy']

Where is that useful? Well, imagine now that your website has four pages named by the paths to those pages. It looks like this:

views =

'/reviews/pool-of-radiance': 121

'/reviews/summer-games': 90

'/reviews/wasteland': 139

'/reviews/impossible-mission': 76

A list of pages from this object is obtained using the following comprehension:

page for page of views

This results in an array containing the page names:

[ '/reviews/pool-of-radiance'

'/reviews/summer-games'

'/reviews/wasteland'

'/reviews/impossible-mission' ]

Comprehending values

To get the property values from an object instead of the property names, use a slightly different comprehension format:

value for property, value of object

For example:

score for name, score of {bob: 152, john: 139, tracy: 209}

# [152, 139, 209]

The number of views for each page is obtained using the comprehension

count for page, count of views

for the four pages described earlier. This results in an array containing the page views for those pages:

[ 121, 90, 139, 76 ]

Alternative format

So far you’ve seen comprehensions written on a single line:

expression for property, value of object

But it’s also possible to have the expression indented underneath:

for property, value of object

expression

When you do that, the expression is evaluated for each property in the object. For example, consider a comprehension to collect and sum the view count for all of the pages:

sum = 0

for page, count of views

sum = sum + count

# [121, 211, 350, 426]

Use the indented style when the expression is long and needs to be on a separate line for readability. Now it’s time to see all of this in action.

4.3.2. Example

To keep track of how many views each page gets, you need a function to increment the value stored against an individual page. Then, to get the total number of views for all pages, you’ll need a function that sums all of the values of the object. The next listing is a first implementation of this program. A detailed discussion follows the listing.

Listing 4.5. Page views

Reduce!

If you were expecting the sum to be done inside the total function with a reduce on the array, then rest assured that you can and should use Array.reduce in CoffeeScript.

Before moving along to more uses of objects, notice in listing 4.5 that there’s a bit of syntax that looks a little foreign. What is the ?= in views[key] ?= 0 used for?

Dealing with undefined properties

Suppose the views object doesn’t yet have any properties. If there’s a page called donatello that receives a view, how do you increment a donatello property that doesn’t exist? The long way to do it is to first use the existential operator to see if it exists, and then initialize it if it doesn’t:

if !views['donatello']?

views['donatello'] = 1

This is a common pattern, so there’s a shorter version of it called existential assignment. Put an existential operator in front of the assignment operator:

views['donatello'] ?= 0

You’ll notice that the viewsIncrement function takes the name of the property as the argument key, which it uses as the property name. The specific case of donatello is then generalized:

views[key] =? 0

There’s one other foreign word, though; what does that own keyword in listing 4.5 do?

Own properties

The total function adds an own to the comprehension, immediately after the for keyword:

for own url, count of views

Objects can get properties that weren’t defined directly on them but somewhere else on the prototype chain. The prototype chain is described later in this chapter, but for now, be mindful that when an object is used as a key-value store, the comprehensions in that object should always include the own keyword.

TIP

If you’re using an object as a key-value store, always use own inside comprehensions on that object.

So far you’ve seen objects used for their key-value properties. By using objects as values, you can represent other structured data in CoffeeScript.

4.4. Structured data

Objects are useful not only for flat key-value data stores but also for data that has multiple levels (such as in listing 4.6). What’s the difference? In a flat key-value store, each key maps to a single number, string, or boolean value (or even null or undefined). In contrast, when one of the keys maps to an object or array, then data can be nested. Think of the difference between a phone book that has names relating only to numbers and a phone book that has names relating to numbers, addresses, and birthdays. In this section you’ll see how to use objects as values in other objects and how this means they can be used for structured data such as trees.

4.4.1. JSON

Imagine you wanted to get the status updates related to a particular topic from a popular social network. When you request data from the social network web service, the response you get back might look something like what’s shown in the next listing.

Listing 4.6. JSON status updates

This format is called JavaScript Object Notation (JSON). Being the language of the web, JavaScript’s syntax for representing structured data has become a popular format for data interchange on the web. JSON is a subset of the literal syntax for objects in JavaScript. This means that valid JSON is a valid object literal in JavaScript and in CoffeeScript. But not every valid object literal is valid JSON. You can expect many web services to offer JSON as one of the supported data formats they provide.

4.4.2. Trees

The data that contains the status updates has a tree structure. For phone numbers, each property is mapped to a single string, shown in figure 4.2.

Figure 4.2. Object as map from names to numbers

The response that you get back from the web service isn’t a flat structure. The object has a property results with a value that is itself an object with three properties defined, each of which has a value that’s another object. This data looks more like a tree, as shown in figure 4.3.

Figure 4.3. Object as tree

You’ll frequently encounter tree structures when writing CoffeeScript programs. Not only is any JSON data received by your application a tree structure, but HTML documents, with their nested elements, are also trees.

Accessing values

For the response variable shown in listing 4.6, the value of the results property can be obtained with the familiar dot notation:

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!" }

# }

To access an object farther down the tree, you can use another dot to refine the selection:

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!"}

Wait a minute!

If you’re thinking that a chain of object calls is a train wreck, then you’re correct in the case of a method call returning another object that is then modified. In this case, though, the object is being used purely as data. All of the data is self-contained, and there’s no indirection occurring.

You’ve seen CoffeeScript objects used as data and transformed with comprehensions. You’ll need to do more with objects, though, and in CoffeeScript this means your objects will need to work with your functions. One piece of glue used with objects and functions is the binding of functions to objects.

4.5. Binding

When working with objects, you’ll need some way to refer to them. One important way this is achieved in CoffeeScript is with binding. Binding in CoffeeScript works the same as in JavaScript, with the important addition of new syntax that makes it easier to deal with.

If you’re coming from other programming languages, the way binding works in JavaScript and CoffeeScript can be counter to your expectations. For that reason, it’s important to start at the beginning and take it slowly. First, remember that a first-class value function can be the value of an object property:

yourObject =

someFunction: ->

When a function is accessed as a property of an object and invoked, then the object takes on a special behavior; it becomes this inside the function invocation:

yourObject.someFunction()

If you look at it, because of the presence of the property access, yourObject is the object on which the function was invoked. Another way to put it is to say that yourObject is the receiver of the function call (it’s receiving the current message). That’s not quite the whole story, though. Binding can be a little nuanced for the beginner, so to get to a full understanding, it’s once again necessary to take a scenic route.

In this section you’ll see why a dynamic this is useful and how to use it. You’ll also see where a dynamic this isn’t useful and how you can stop this from being dynamic by using the fat arrow. That’s a head-spinning sentence! Sit back, relax, and take the scenic route. What is this?

4.5.1. this

Imagine you have an element in an HTML document and you want to change the contents of the element when it’s clicked. This venerable problem, which you’ve seen before, has been around since the beginning of JavaScript. Here’s the minimal HTML5 document:

<!doctype html>

<title>How many people are coming?</title>

<body>

<div id='#some-element'>Change this text</div>

Now suppose you assign a click handler:

someElement = document.querySelector('#some-element')

someElement.onclick = ->

someElement.innerHTML 'Got clicked!'

That works okay, but what if there are two elements?

<!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>

Now your handlers will look like this:

someElement = document.querySelector('#some-element')

someOtherElement = document.querySelector('#some-other-element')

someElement.onclick = ->

someElement.innerHTML = 'Got clicked!'

someOtherElement.onclick = ->

someOtherElement.innerHTML = 'Got clicked!'

You can smell the duplication! If the behavior is the same when the two elements are clicked, then you should be able to define one function that handles both events. How can you write a single function that works for both events?

innerHTML vs. html()

The innerHTML property is a standard property according to the W3C specification. If you are familiar with jQuery, you will know that it defines an html method that can be used to set the HTML content of a DOM element. In either case, it’s a library method that allows you to change something about an object in an HTML document. Objects in HTML documents are special objects called host objects that you will learn more about in chapter 11.

Elements are receivers

Every time a function is invoked, some object is the receiver for that function invocation. In many cases, the receiver is unimportant to you, but when you’re handling an event, the object that’s receiving the event is important. When a function is handling a click event on an element in an HTML document, then the receiver is the element that was clicked, as shown in figure 4.4.

Figure 4.4. The receiver of a click event

Inside the handling function, the receiver is available using the @ symbol or the this keyword. It can be helpful to think of @ in terms of the object you are at or this in terms of this object that is being used, right now.

Using the receiver, you can now rewrite the duplicated event handlers so that instead of having two handlers you can have just one. First, rewrite one event handler to use the receiver. Instead of this

someElement.onclick = ->

someElement.innerHTML = 'Got clicked!'

it will look like this:

someElement.onclick = ->

this.innerHTML = 'Got clicked!'

But this can get tedious to type, so CoffeeScript syntax lets you write it with @:

someElement.onclick = ->

@innerHTML = 'Got clicked!'

Although you can put a dot after @, it’s unnecessary:

Use this technique to create a click-handling function that you can attach to both elements:

The value of the receiver can be subtle. It’s time to review.

How to know what the receiver is

If you want to determine what the value of this is going to be, think about which object is the receiver. In the case of the previous click event, consider that the onclick function has been invoked on the element:

One strategy, then, is to look at the dot in front of wherever the function has been assigned. In general, though, the best way to tell the value of this is to ask yourself, “Who is receiving this event?”

Sometimes you don’t want the object that’s the receiver of the function. Sometimes you want some other object. That’s what the fat arrow is for.

4.5.2. The fat arrow

Imagine you now want the text on your HTML element to be changed to “Got clicked” after one second, instead of immediately. To do this, you first attach a click handler and then invoke setTimeout inside it, passing it a function and a number of milliseconds:

Try that in your browser; it doesn’t work. That’s because the function being invoked is the one that you gave as an argument to setTimeout.

Take a step away from this specific example for a moment and consider it in a more abstract sense. Think of an object with two properties, one being a single method that returns the object itself:

When you invoke the itself method, it will return the original object. This means you can get the value by first invoking the itself method to get the object back again and then access the property:

noumenon.itself().value

# 'noumenon'

This example is contrived, but having a method return the object it was called on is a powerful technique (explored in chapter 7). The important thing to consider here is what happens if the function is used somewhere else:

detachedFromObject = noumenon.itself

detachedFromObject()

# 'undefined'

The value of this is different. It’s undefined! The same will happen if you assign the function to the property of another object and make it a method of that object:

other =

value: 'other'

other.itself = noumenon.itself

other.itself().value

# 'other'

Remember that functions are first-class values? This is the price you have to pay for all that expressive power with dynamic object binding. There’s no such thing as a free lunch.

In CoffeeScript, a function never belongs to an object in the same way that methods belong to objects in other object-oriented languages. Instead, a function is bound dynamically to the receiving object when invoked. If you pass what you think is a method of an object as a callback function, you’re passing a bare-naked first-class function.

If it’s still not completely clear, you can experiment with a version of this example on the REPL. First, change the itself method to console.log so you can see what’s happening:

noumenon.itself = -> console.log @value

Now pass the function to setTimeout:

setTimeout noumenon.itself, 1000

# 'undefined'

As before, the function is being invoked with a different receiver. In the case of setTimout, because it’s a global method, the function will be invoked with that global object as the receiver. The question is, with a dynamic receiver object, how do you get a reference to your original object inside a function when it’s used as a callback or otherwise called with some different object as the receiver?

Using scope

One way to get a reference to the element that you want is by using function scope. For example, the function that’s being invoked by setTimeout is lexically inside the function invoked as the click handler. Closure means that any variables defined in the outer function will be visible to the inner function:

When you assign the @ reference to a variable, the other function defined inside the scope has access to it.

Using the fat arrow

The fat arrow provides a way to keep a lexical reference to this without having to use an intermediate variable such as clickedElement in the previous example. Controlling the value of the receiver is called function binding. Using a fat arrow, you can rewrite the previous example:

The fat arrow causes the function to have this bound lexically at the point in the code where it appears. This has uses other than handling browse events, which you’ll learn more about later, after you’ve learned more about objects.

So ends the scenic route through binding. From here, you move on to the foundation of CoffeeScript objects: prototypes.

4.6. Prototypes

CoffeeScript is a prototype-based language. Every object (except null—you’ll learn why later) has a link to another object called its prototype. Properties defined on the prototype of an object are also available on the object itself. A prototype-based object system will be unfamiliar to those who’re used to the class-based object systems found in most popular programming languages because it supports object creation and inheritance without requiring any classes at all.

In this section you’ll learn what it means to create a new object using an existing object as the prototype and how an object inherits properties from its prototype. To understand this, it’s important to distinguish inheritance from copying.

4.6.1. Copy

Back when people wore leg warmers, fluorescent clothes, and their hair in a perm, music was distributed on small plastic cartridges called cassettes. Imagine your friend Corey has lent you a cassette he made that contains some of his favorite songs:

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"

Making a copy

You really like the cassette and decide to create a copy of it:

cassetteCopy = {}

for own property, value of cassette

cassetteCopy[property] = value

Your cassette now has all of the tracks from Corey’s cassette:

cassetteCopy.track3

# "Electric Avenue – Eddy Grant"

You give Corey his cassette back and he decides to add another song:

cassette.track5 = "Rock Me Amadeus - Falco"

Sadly, your copy does not have the new song:

cassetteCopy.track5?

# false

If only there were a way to have a cassette that would automatically have access to any updates from Corey’s cassette. There is—use Corey’s cassette as the prototype for your own cassette.

4.6.2. Object creation

When you use one object as the prototype of another object, the properties from the prototype aren’t copied; instead, they’re found via lookup when the property is accessed. Creating an object by using an existing object as the prototype causes the new object to have a permanent link back to the prototype. A copy of an object doesn’t have this feature.

The Object.create method is one way to create an object using an existing object as its prototype. When a property is accessed on the new object but isn’t found, the prototype link is followed and the property is looked for on the prototype object. The property is inherited from the prototype.

Object.create

Although the Object.create method is part of the fifth edition of the ECMAScript standard, it isn’t available in all environments. In particular, versions of Internet Explorer prior to IE9 don’t have Object.create. A version of Object.create that you can use when targeting environments that don’t have one built in is found in chapter 11. Exactly what Object.create does is also covered in that chapter.

In terms of the cassette, imagine that instead of copying it, you have a small wireless music device that can link itself to Corey’s cassette. Once you’ve linked the device, playing it will cause it to communicate with Corey’s cassette and play the tracks on there. Create this device in CoffeeScript:

musicDevice = Object.create cassette

Now you automatically get updates from Corey’s cassette:

cassette.track6 = "Sledgehammer – Peter Gabriel"

musicDevice.track6 is "Sledgehammer – Peter Gabriel"

# true

The prototype link goes in only one direction. Changing a track directly on musicDevice will update it but won’t affect the cassette:

musicDevice.track1 = "Toy Soldiers - Markita"

musicDevice.track1 is "Toy Soldiers - Markita"

# true

The original cassette remains unchanged:

cassette.track1 is "Safety Dance - Men Without Hats"

# true

In a similar fashion, adding a new track to musicDevice won’t affect cassette:

musicDevice.track7 = " Buffalo Stance - Neneh Cherry"

cassette.track7?

# false

You can see the dynamic nature of the relationship between objects and their prototypes in figure 4.5. Creating objects from other objects is a simple but powerful mechanism.

Figure 4.5. A prototype example

4.6.3. Exercises

Time to recap what you know about prototypes:

· Create another music device with musicDevice as its prototype.

· Add a new song to Corey’s cassette. Can you access it on the new music device? Why?

· Add a new song to musicDevice. Can you access it on the new music device? Why?

4.7. Behavior

So far the objects presented have contained only data. When you created an object to hold data about page views, you also created a helper function that acted on the data structure. If a function is going to change an object, there’s a very good chance that it should belong to the object. In general, objects should be responsible for their own data. This means the views object should own the data it contains and be responsible for manipulating it. As you’ve learned, a function is called a method when it’s called against an object. The methods of an object provide the behavior of that object.

In this section you’ll learn that creating objects from a prototype can lead to more elegant code. First, consider the page views program from section 4.3.2 and what will happen when you need to extend it.

4.7.1. Refactor

In the first page views program (listing 4.5), there’s a single views object. Both the increment function and the total function act on the views object by knowing which variable it’s assigned to. If you wanted to track views for two different websites, then you could manually create twoviews objects:

businessViews = {}

pleasureViews = {}

Then you could change increment and total so that the object they’re acting on is passed in as an argument:

Then to add a view to businessViews, you’d do this:

increment businessViews, '/products/fade-o-meter/enterprise-edition'

Now increment is modifying the businessViews object. To be clear, you should avoid state where possible, as chapter 6 will help you to understand. If you really need state, then it’s best to contain the state and have the object that owns the state be responsible for changing it. With that in mind, here’s a new version of the page views code.

Listing 4.7. Page views revisited

Now create two view counters:

businessViews = Object.create views

personalViews = Object.create views

If you increment the value for just one URL on that object 100 times, then the total is 100. But the other object maintains its own state separately. It isn’t affected by increments to the first object:

for i in [1..100] by 1

businessViews.increment '/product-details/2454'

businessViews.total()

# 100

personalViews.total()

# 0

Methods

When a function is invoked on an object (when that object is this), call it a method. The views prototype has three methods: clear, increment, and total.

The views prototype has a clear method that sets the pages property to an empty object. If using existing objects as prototypes creates objects, then existing state on those objects may need to be removed. There are other techniques for managing this, from classes and constructors, which you’ll learn about in the next section, to separation of state from behavior with other techniques, which are covered in chapters 6 and 7.

Objects in CoffeeScript are open by default. Having used prototypal inheritance to set up the object relationships, a method can be made available to both of the views objects by updating the prototype. Here’s a pages method that will return the number of pages that have views against them:

views.pagesTotal = ->

(url for own url of @).length

businessViews.pagesTotal()

4.7.2. Exercise

Some of the pages on your website aren’t interesting and you don’t want to track them. Add an ignore method to views from listing 4.7 that takes the name of a page and adds it to an array of pages that shouldn’t be tracked.

4.8. Classes

The prototype-based object system in CoffeeScript (and JavaScript) is minimal and low-level, not providing some of the built-in comforts for structuring objects that some other languages have. Although there are many techniques for managing objects using prototypes, and you’ll learn several of them in upcoming chapters, classes are one well-known technique for managing objects. CoffeeScript has dedicated syntax for creating and using classes on top of the underlying prototype-based object system.

The clear method on views is a potential initialization step you may want to do for many objects. The underlying JavaScript uses a constructor function for this purpose. When used with the new keyword, a constructor function creates and initializes an object in JavaScript:

function Views () {

this.pages = {};

}

businessViews = new Views();

Unfortunately, constructor functions in JavaScript are awkward and confusing. Regardless, if you’re creating many of the same sorts of objects, you’ll want three features: a simple declarative syntax for defining them, a way to specify a constructor function for initialization, and optimized performance when creating a large number of objects. That’s what classes in CoffeeScript give you.

In this section you’ll see how to use this syntax to declare classes. You’ll also see that objects aren’t equal, regardless of whether they were created from the same class. First, how do you declare a class?

4.8.1. Declaration

CoffeeScript provides a class syntax that cleans up some of the awkwardness in JavaScript constructors and handles setting up links to prototype objects. If you were writing a system that was capturing the views for many different sites and wanted to define a class for views, it could be implemented as shown in the following listing.

Listing 4.8. A page views class

Comparing listing 4.8 to the prototype-based version from listing 4.7, you’ll notice some new keywords: class, constructor, and new.

Class declaration

The class keyword, followed by a class name, begins the declaration of a class. A class declaration contains methods defining what an object created from the class does. The smallest valid definition of a class is just the class keyword with a name:

class Empty

new operator

Objects can be created from a class using the new operator:

empty = new Empty

constructor method

The constructor method for a class is a function that’s called whenever a new object is created from the class. When a new object is created from the Views class,

newViewsObject = new Views

then the constructor is called, in this case assigning an empty object to the pages property of the new object.

The example in listing 4.8 is similar to that in listing 4.7. The conceptual difference is that instead of creating an object out of another object, you create an object out of a class. This means you’ve now learned three ways to create objects in CoffeeScript: ex nihilo, using another object, and using a class.

If you’re wondering at this stage how classes and constructors relate to the underlying JavaScript, then try compiling the examples in this section and having a look. The veil will be lifted in chapter 5, where the relationship of CoffeeScript syntax to the JavaScript object model is explored in detail.

4.8.2. Object identity

Objects are never equal to each other; an object is equal only to itself:

businessViews is personalViews

# false

Equality will be true for variables referencing objects only if they refer to the same object:

objectReferenceOne = {}

objectReferenceTwo = objectReferenceOne

objectReferenceTwo is objectReferenceOne

# true

Two objects created from the same class are not equal:

class Black

green = new Black

Now green is a new Black but it is never the new Black:

green is new Black

# false

Similarly, businessViews and personalViews aren’t equal because they don’t reference the same object:

businessViews is personalViews

# false

Prototypes or classes?

It depends. For the page views example, the objects are all going to have the same functionality and they don’t need to change while the programming is running. Given that, when the page views example is extended into a full web application later, the first approach will be class-based.

4.8.3. Exercises

Try these exercises to help you understand classes in CoffeeScript:

· Define a class for a GranTurismo object with a constructor that takes an options object as an argument and assigns all of the options supplied as properties on @ (this).

· Add a summary method to the GranTurismo class that returns a summary of the options passed to the constructor as a string.

4.9. Putting it together

Listing 4.9 provides the full page view application. To view the current hit count for both personal and business, run the application from the command line:

> coffee 4.9.coffee

Once the application is running, visit the application homepage at http://127.0.0.1:8080/ in your browser, and you’ll see the text “Personal: 0 Business: 0.” Now, each time you visit a URL path such as /personal/1 or /business/1 in your browser, the hit will be recorded. Go ahead and visit /personal/1 five times and then visit the application homepage again. You’ll see “Personal: 5 Business: 0.”

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'

The class syntax used in listing 4.9 isn’t the only way to write this application. It’s simply one approach that you’ve seen emerge from the CoffeeScript syntax and object system throughout this chapter.

4.10. Summary

JavaScript has a powerful object literal syntax that makes objects effective as generic data containers. The power of this literal syntax is evident in the rising popularity of JSON as a data exchange format for web applications. CoffeeScript takes full advantage of this syntax and, if you don’t like using curly braces even for object literals, the YAML-style syntax provides an even more succinct alternative.

The prototype-based object model has always been misunderstood in JavaScript. By exploring prototypes first and coming to CoffeeScript’s class syntax later as an organizing technique, you have a glimpse into the conceptual elegance of the little-known world of prototype-based object systems.

In the next chapter you’ll look deeper into the JavaScript object model and see how CoffeeScript syntax exposes the elegance and expressiveness within, without getting in your way. At the same time you’ll learn composition techniques and patterns for both prototype-based and class-based approaches.