Chapter 1. Core Concepts

A program is a set of written instructions to be executed (i.e., carried out) by a computer or a software application. The written, human-readable text of a program is called source code, or just code. The person who creates a program is called a programmer, a coder, or a developer. Every program is written in a particular programming language, just as every book is written in a particular language (English, Russian, Japanese, etc.). Programming languages dictate the syntax and grammar that programmers must use to form the instructions in a given program. This book provides from-the-ground-up coverage of the syntax, grammar, and usage of one specific programming language, ActionScript 3.0. Get ready for a good time.

Tools for Writing ActionScript Code

ActionScript code is written in plain text, so an ActionScript program can be created with nothing more than a simple text editor, such as Notepad on Windows or TextEdit on Macintosh. However, most ActionScript programmers write ActionScript code using one (or both) of two commercial tools produced by Adobe Systems Incorporated: Flex Builder and the Flash authoring tool.

Flex Builder is an integrated development environment, or IDE. An IDE is an application for writing and managing code, much as a word processor is an application for creating printed documents. Developers use Flex Builder to create software applications and multimedia content using either ActionScript or MXML, or both. MXML is an XML-based language for describing user interfaces.

By contrast, the Flash authoring tool is a hybrid design, animation, and programming editor. Developers use the Flash authoring tool to create software applications and multimedia content by combining ActionScript code with manually drawn graphics, animation, and multimedia assets.

ActionScript 3.0 is supported by Flex Builder 2 or higher, and Flash CS3 (Version 9 of the Flash authoring tool) or higher. To obtain a copy of Flex Builder, visit http://www.adobe.com/products/flex/productinfo/overview/. To obtain a copy of the Flash authoring tool, visit http://www.adobe.com/go/flash/.

The vast majority of this book concentrates on the creation of software applications and multimedia content using pure ActionScript (i.e., code only). Chapter 29 covers the use of ActionScript in the Flash authoring tool. This book specifically does not include coverage of MXML. For coverage of MXML, see O’Reilly’s Programming Flex 2 (Kazoun and Lott, 2007) and Adobe’s Flex Builder documentation.

Flash Client Runtime Environments

ActionScript programs can be executed by three different software applications (all produced by Adobe): Flash Player, Adobe AIR, and Flash Lite.

Flash Player executes ActionScript programs in a web browser or in a standalone mode on the desktop. Flash Player has very little access to the operating system (e.g., it cannot manage files, control windows, or access most hardware).

Adobe AIR executes ActionScript programs on the desktop and has full integration with the desktop operating system (e.g., can manage files, control windows, and access hardware).

Flash Lite executes ActionScript programs on mobile devices, such as cellular phones. As of the publication of this book, Flash Lite can execute ActionScript programs written in ActionScript 2.0, but not ActionScript 3.0, while Flash Player and Adobe AIR can execute programs written in ActionScript 3.0. Therefore, the techniques taught in this book apply to Flash Player and Adobe AIR, but will not apply to Flash Lite until it adds support for ActionScript 3.0.

In generic terms, Flash Player, Adobe AIR, and Flash Lite are all known as Flash client runtime environments (or Flash runtimes for short) because they manage ActionScript programs while they execute, or “run.” Flash runtimes are available for Windows, Macintosh, and Linux, as well as a variety of different mobile hardware devices. Because ActionScript programs are executed by a Flash runtime, not a specific operating system or hardware device, each ActionScript program is considered portable because it can run on different hardware devices (phones, game consoles) and operating systems (Windows, Macintosh, and Linux).

In casual discussion, the term ActionScript virtual machine is sometimes used as an equivalent for Flash client runtime environment. There is, however, a difference between these two terms, so they should not be used interchangeably. The ActionScript virtual machine (AVM) is technically the software module inside Flash Player, Adobe AIR, and Flash Lite that executes ActionScript programs. But each Flash runtime also has other responsibilities, such as displaying content on screen, playing video and audio, and communicating with the operating system. The specific version of the ActionScript virtual machine that runs ActionScript 3.0 code is known as AVM2. The specific version of the ActionScript virtual machine that executes ActionScript 1.0 and ActionScript 2.0 code (not covered in this book) is known as AVM1.

Compilation

Before an ActionScript program can be executed by a Flash runtime, it must be converted from human-readable ActionScript 3.0 code to a condensed, binary format that Flash runtimes understand, known as ActionScript bytecode, or ABC. On its own, however, ActionScript bytecode cannot be executed by Flash runtimes; instead, it must be wrapped in a binary container file known as a .swf file. The .swf file stores the bytecode and any embedded media assets required by the ActionScript program in Flash file format, or SWF. The process of converting an ActionScript program to bytecode is known as compiling the program. The process of generating a .swf file is known as compiling the .swf file, or sometimes, exporting or publishing the .swf file.

To compile ActionScript 3.0 programs and .swf files, we use a software module known as a compiler. A compiler that compiles ActionScript code is known as an ActionScript compiler. A compiler that generates .swf files is known as a SWF compiler. Any SWF compiler that claims full support for the Flash file format includes an ActionScript compiler. Naturally, both Flex Builder 2 and the Flash authoring tool include a SWF compiler (and, by extension, an ActionScript compiler). Flex Builder 2 and the Flash authoring tool share the same ActionScript compiler but have different SWF compilers—known, respectively, as the Flex compiler and the Flash compiler. Adobe also offers the Flex compiler as a standalone command-line application called mxmlc. The mxmlc compiler is included in Adobe’s free developer’s toolkit, the Flex 2 SDK, available at http://www.adobe.com/go/flex2_sdk.

Just-In-Time Compilation

When an ActionScript program runs, the Flash runtime reads compiled ActionScript bytecode and translates it into native machine-code instructions that are executed by the specific computer hardware on which the program is running. In many cases, the native machine-code instructions are saved so they can be used again without the need to be retranslated from ActionScript bytecode.

Just as converting ActionScript 3.0 code to bytecode is called compiling, the process of translating ActionScript bytecode into native machine code and then saving that machine code for later execution is, likewise, known as compiling. Hence, most ActionScript code undergoes two levels of compilation. First, the developer compiles the code from human-readable format to a format understood by the Flash runtime (ActionScript bytecode). Then, the Flash runtime automatically compiles the ActionScript bytecode to a format understood by the hardware running the program (native machine code). The latter form of compilation (bytecode to machine code) is known as just-in-time compilation, or JIT, because it happens immediately before the specific bytecode being compiled is needed by the program. Just-in-time compilation is sometimes also called dynamic translation. Experienced programmers may be interested to know that code at the top level of a class definition is not just-in-time compiled (because it is executed only once).

Quick Review

The past several pages covered a lot of ground. Let’s review what we’ve covered so far.

An ActionScript program is a set of instructions to be executed by one of the Flash runtimes: Flash Player, Adobe AIR, or Flash Lite. ActionScript programs can be written in a text editor, Flex Builder, or the Flash authoring tool. In order to run an ActionScript program, we must first compile it into a .swf file using a SWF compiler such as the Flash compiler included with the Flash authoring tool, or mxmlc, which is included with both Flex Builder 2 and the Flex 2 SDK.

Don’t worry if some of the preceding concepts or terms are new to you. We’ll be applying them abundantly over the next 900-plus pages.

Now let’s write some code!

Classes and Objects

Imagine you are going to build an airplane, entirely from scratch. Think about the process you would follow. You very likely wouldn’t just head to a metal shop and start welding. You’d have to draw up a blueprint for the airplane first. In fact, given that you are building the airplane from scratch, you’d have to draw up not just one, but many blueprints—one for each of the airplane’s many parts (the wheels, the wings, the seats, the brakes, and so on). Each blueprint would describe a specific part conceptually and correspond to an actual part in the physical incarnation of the airplane. To build the airplane, you would manufacture each of the parts individually, and then assemble them according to a master blueprint. The interoperation of the airplane’s assembled parts would produce the airplane’s behavior.

If that all sounds logical to you, you’ve got what it takes to become an ActionScript programmer. Just as an airplane flying through the sky is a group of interoperating parts based on a set of blueprints, a running ActionScript program is a group of interoperating objects, based on a set of classes. ActionScript objects represent both the tangible things and the intangible concepts in a program. For example, an object might represent a number in a calculation, a clickable button in a user interface, a point in time on a calendar, or a blur effect on an image. Objects are incarnations, or instances, of classes. Classes are the blueprints upon which objects are based.

The first step in writing a new program is determining its classes. Each class describes, in code, both the characteristics and behavior of a particular type of object. Some of the classes in a program must be written from scratch, while others are provided by ActionScript and the various Flash runtimes. Classes written from scratch (known as custom classes) are used to produce specialized types of content, such as an order form for a shopping application, a car in a racing game, or a message in a chat application. By contrast, classes provided by ActionScript and the various Flash runtimes (known as built-in classes) are used to perform fundamental tasks such as creating numbers and text, playing sounds, displaying images, accessing the network, and responding to user input.

From the classes in a program, we make (or instantiate) objects and then tell those objects what to do. What the objects do determines the behavior of the program.

Tip

Building a program with classes and objects is known as object-oriented programming (OOP).

In the next section we’ll start writing an actual program, but before we do, let’s take a brief look at an important group of classes, known as native classes, that are built directly into ActionScript. The native classes, listed in Table 1-1, are used to manipulate basic types of information, such as numbers and text. You can expect to use instances of at least one or two of the native classes in every program you write—much like you might use ready-made parts from a third-party supplier when building an airplane. Read over Table 1-1 for basic familiarity. In the coming chapters, we’ll study the native classes in much more detail.

Table 1-1. ActionScript’s native classes

Class

Description

String

Represents textual data (i.e., a string of characters)

Boolean

Represents the logical states true and false

Number

Represents floating-point numbers (i.e., numbers with a fractional value)

int

Represents integer numbers (i.e., numbers with no fractional value)

uint

Represents positive integer numbers

Array

Represents an ordered list

Error

Represents a program error (i.e., a problem in your code)

Date

Represents a specific point in time

Math

Contains common mathematical values and operations

RegExp

Defines tools for searching and replacing text

Function

Represents a reusable set of instructions that can be executed, or called, repeatedly

Object

Defines the basic features of every object in ActionScript

Now let’s try using classes and objects in an example program—a simple simulated zoo game with virtual pets.

Tip

Using the technique known as timeline scripting in the Flash authoring tool, it is possible to create an ActionScript program without first creating a class (see Chapter 29). However, even if you never expect to create classes yourself, you should still study the techniques presented in this chapter. Knowing how classes are created will greatly deepen your understanding of ActionScript and make you a better programmer.

Creating a Program

As we just learned, ActionScript programs are made up of classes, which are the blueprints for the interoperating parts (objects) of a program. Typically, the development of a new ActionScript program starts with a design phase, during which the program’s functionality is broken into a logical set of classes. Each class is given a name, a set of features, and a role in the larger program. One class in particular is designated as the main class. The main class provides the starting point, or program point of entry, for the application. To start a new program, the Flash runtime automatically creates an instance of the program’s main class.

For our virtual zoo example program, we’ll name the main class VirtualZoo. As the first step in building the program, we’ll create a folder on the filesystem, named virtualzoo. Within that folder, we’ll create a subfolder named src (short for source) in which to store all .as files (i.e., all files containing source code).

Each program’s main class code must be placed in a text file named after the main class, and given the extension .as. Accordingly, we’ll create an empty text file named VirtualZoo.as. Notice that the filename VirtualZoo.as exactly matches the class name VirtualZoo and that case sensitivity matters. We’ll place VirtualZoo.as in the folder virtualzoo/src. Here’s the file structure for our program’s source files so far:

virtualzoo
   |- src
      |- VirtualZoo.as

With VirtualZoo.as created, we can start writing the VirtualZoo class. However, first we must deal with a potential problem—if our chosen main class name conflicts with (i.e., is the same as) one of ActionScript’s built-in classes, then ActionScript won’t let us create the class, and our program won’t be able to start. To prevent potential naming conflicts in our program, we use packages.

Tip

There is a lot of ground to cover, so we won’t actually compile our zoo program’s code until Chapter 7. If you decide to jump ahead and compile the examples presented in Chapter 1 through Chapter 6, you are likely to encounter various warnings and errors. After Chapter 7, you’ll be able to compile all versions of the example program without errors.

Packages

Like its name suggests, a package is a conceptual container for a group of classes and, as we’ll learn later, for other things in a program. Each package delimits an independent physical region of a program and gives that region a name, called the package name. By convention, package names typically start with a lowercase letter while class names typically start with an uppercase letter. This helps distinguish package names from class names.

When a class’s source code resides within a package, that class automatically adopts the package’s name as part of its own name, much like a child takes on his parents’ family name. For example, a class named Player in a package named game becomes known as game.Player. Notice that the package name comes first and is separated from the class name using a period (.) character (character is simply programming jargon for letter, number, punctuation, and so on). The package name helps distinguish the game.Player class from other classes also named Player, thus preventing name conflicts between different parts of a program or between a program’s custom classes and ActionScript’s built-in classes.

To create a new package, we use a package definition directive. Let’s dissect that term. In ActionScript, all program instructions are known generally as directives. Definitions are one type of directive; they create, or define something, such as a package or a class. In this case, the thing being defined is a package, hence the term, package definition directive.

Tip

A definition that creates something in a program is said to define or declare that thing. Definitions are sometimes also referred to as declarations.

Here’s the general form of a package definition directive:

package packageName {
}

All package definitions start with a keyword: package. A keyword is a command name reserved for use by the ActionScript language. In this case, the package keyword tells ActionScript to create a package. After the package keyword, we provide the desired package name, represented by packageName in the preceding code. (Throughout this book, italicized code, such as packageName, indicates text that must be replaced by the programmer.) Next, we mark the beginning and end of the package contents using curly braces: { and }. To add a class to a package, we insert its source code between the curly braces, as follows:

package packageName {
  Class source code goes here
}

In technical terms, the curly braces in a package definition are a kind of statement, known as a block statement. Like definitions, statements are a kind of directive, or basic program instruction. A block statement marks the beginning and end of a group of directives that should be treated as a logical whole. A package definition’s block statement is known as a package block or sometimes package body.

Tip

For a complete list of ActionScript statements, see Chapter 10.

By convention (but not necessity), package names typically have the following structure:

  • The reversed domain name of the organization creating the program

  • Followed by a period (.)

  • Followed by the general purpose package’s contents

For example, a package containing classes for a mapping application created by Acme Corp., whose domain name is acme.com, might be named com.acme.map, as shown in the following code:

package com.acme.map {
}

Notice that com precedes acme (i.e., in the package name, the domain name is reversed).

Tip

Domain names are guaranteed to be unique by the system of authorized top-level-domain registrars; thus, starting your package names with your organization’s domain name avoids name conflicts with code developed by other organizations.

Now let’s try using packages in our virtual zoo program. To keep our example simple, we’ll use the package name zoo, without any leading domain name. To define the zoo package, we’ll add the following code to the file VirtualZoo.as:

package zoo {
}

Now that we’ve added a package to the file VirtualZoo.as, we must change that file’s location on the filesystem to match the package it contains. Due to a requirement imposed by all Adobe’s ActionScript compilers, when a source file contains a class (or other definition) in a package, it must reside in a folder structure that matches that package name. For example, a file that contains a package named com.gamecompany.zoo must reside in a folder named zoo, in a folder named gamecompany, contained by a folder named com (i.e., com/gamecompany/zoo). Accordingly, we’ll create a new folder named zoo in our program’s file structure and move VirtualZoo.as into it. The file structure for our program’s source files becomes:

virtualzoo
   |- src
      |- zoo
         |- VirtualZoo.as

Now that we have a package definition, let’s add the VirtualZoo class to it.

Defining a Class

To create a new class, we use a class definition, as shown in the following generalized code:

class Identifier {
}

A class definition starts with the keyword class, followed by a class name, represented by Identifier in the preceding code. The term identifier simply refers to a name. Identifiers must not contain spaces or dashes, and cannot start with a number. Class names conventionally use a capital letter for the first, and all subsequent words in the name, as in Date or TextField. (TextField is a built-in Flash-runtime class whose instances represent text that can be displayed on screen.)

The curly braces ({}) following Identifier in the preceding class definition are a block statement, just like the block statement in a package definition. A class definition’s block statement is known as the class block or sometimes the class body. The class block contains directives that describe the characteristics and behavior of the class and its instances.

Here’s the basic class definition for the main class of our simulated zoo game, VirtualZoo. We place the class definition in the package body, in the file VirtualZoo.as:

package zoo {
  class VirtualZoo {
  }
}

Because the preceding VirtualZoo class definition resides in a package named zoo, the complete name of the class (known as the fully qualified class name) is zoo.VirtualZoo. In casual discussion, however, we’ll use the shorter, unqualified class name, VirtualZoo.

Now that we have our program’s main class defined, let’s create one of the other classes in the program—VirtualPet. From the VirtualPet class, we’ll create objects representing pets in the zoo.

Like VirtualZoo, we’ll place the source code for the VirtualPet class in the zoo package, in its own file named VirtualPet.as saved in the zoo folder. Here’s the code from the VirtualPet.as file:

package zoo {
  class VirtualPet {
  }
}

Notice that a package definition can span multiple source files. Even though VirtualZoo and VirtualPet are stored in different .as files, they belong to the same package, zoo. Any class in any file that resides in a package named zoo is considered part of the zoo package. By contrast, a class definition cannot span multiple files; it must be written, in its entirety, within a single file.

Access Control Modifiers for Classes

By default, a class in a given package can be used by code that also resides in that package only. To make a class available for use outside the package in which it is defined, we must define it with the public attribute. In general terms, a class’s attributes dictate how the class and its instances can be used in a program. Attributes are listed before the keyword class in a class definition, as shown in the following generalized code:

attribute class ClassIdentifier {
}

For example, to add the public attribute to the VirtualPet class, we’d use:

package zoo {
  public class VirtualPet {
  }
}

However, in the case of VirtualPet, the public attribute is unnecessary because VirtualPet is used by the VirtualZoo class only, and VirtualZoo can use the VirtualPet class because both classes reside in the zoo package (classes in the same package can always access each other). Hence, we can return to our original VirtualPet definition, which implicitly allows VirtualPet to be used within the zoo package only:

package zoo {
  class VirtualPet {
  }
}

If we wish to explicitly indicate that we intend VirtualPet to be used within the zoo package only, we can use the internal attribute, as shown in the following code:

package zoo {
 internal class VirtualPet {
  }
}

A class defined with the internal attribute can be used within its containing package only. That is, defining a class with the internal attribute is identical to defining the class with no access-control modifier at all. The internal attribute simply serves to make the programmer’s intention unambiguous.

The internal and public attributes are known as access-control modifiers because they control the region within which a class can be used (accessed) within a program.

Unlike the VirtualPet class, the VirtualZoo class must be defined with the public attribute because it is the application’s main class.

Tip

Adobe’s compilers require an application’s main class to be defined with the public attribute.

The following code updates VirtualZoo to include the necessary public attribute:

package zoo {
  public class VirtualZoo {
  }
}

Virtual Zoo Review

Our game now has two classes: VirtualZoo (the main class) and VirtualPet (which represents the pets in the game). The classes reside in the package zoo, and are stored in plain-text files named VirtualZoo.as and VirtualPet.as, respectively. By requirement of Adobe’s ActionScript compilers, VirtualZoo is defined with the public attribute because it is the application’s main class. By contrast, VirtualPet is defined with the internal attribute, so it can be used inside the zoo package only.

Example 1-1 shows the code for our game so far. The example also introduces something new—code comments. A code comment is a note meant to be read by programmers only and is completely ignored by the compiler. ActionScript code comments come in two varieties: single line, which start with two slashes (//), and multiline, which start with the character sequence /*, and end with the character sequence */.

This is a single-line comment:

// No one here but us programmers

This is a multiline comment:

/*
No one here
but us programmers
*/

The current code for our zoo game follows.

Example 1-1. Zoo game
// Contents of the file VirtualZoo.as
package zoo {
  public class VirtualZoo {
  }
}

// Contents of the file VirtualPet.as
package zoo {
  internal class VirtualPet {
  }
}

Now let’s carry on with the development of our program, starting with the constructor method of our main class, VirtualZoo.

Constructor Methods

A constructor method (or, constructor, for short) is a discrete set of instructions used to initialize the instances of a class. To create a constructor method, we use a function definition within a class block, as shown in the following generalized code:

class SomeClass {
  function SomeClass () {
  }
}

In the preceding code, the keyword function begins the constructor method. Next comes the constructor method name, which must exactly match the class name (case sensitivity matters!). The constructor method name is followed by a pair of parentheses that contain a list of constructor parameters, which we’ll study later. The curly braces ({}) following the parameter list are a block statement, just like the block statements in package and class definitions. A constructor method’s block statement is known as the constructor body. The constructor body contains the directives that initialize instances. Whenever a new instance of SomeClass is created, the directives in the constructor body are executed (sequentially, from top to bottom). Executing the directives in the constructor body is known as executing the constructor or, more casually, running the constructor.

Tip

Constructor methods are created using the function keyword because they are, technically speaking, a type of function. We’ll study functions in Chapter 5.

When a class does not define a constructor function explicitly, ActionScript automatically provides a default constructor that performs no initialization on new instances of the class. Despite this convenience, as a best practice, always include a constructor, even if it is just an empty one. The empty constructor serves as a formal indication that the class design does not require a constructor and should be accompanied by a comment to that effect. For example:

class SomeClass {
  // Empty constructor. This class does not require initialization.
  function SomeClass () {
  }
}

Unlike classes, the accessibility of constructor methods cannot be controlled with access-control modifiers. In ActionScript 3.0, all constructor methods are implicitly considered public. (Future versions of ActionScript might, however, allow for non-public constructor methods.) As a matter of style, this book always includes the public access-control modifier when defining constructor methods, stressing the fact that all constructor methods must be public. The following code demonstrates:

class SomeClass {
  public function SomeClass () {
  }
}

Tip

The rule that constructor methods must be public in ActionScript 3.0 was instituted due to engineering time constraints and volatility of the ECMAScript 4 Language Specification. For details, see Sho Kuwamoto’s article at: http://kuwamoto.org/2006/04/05/as3-on-the-lack-of-private-and-protected-constructors. (Sho is Adobe’s Flex Builder 2’s development team lead.)

The constructor method of an application’s main class plays a special role in a program. It provides an opportunity to execute code immediately after the application has started. As such, the constructor method of an application’s main class is considered the program point of entry.

The following code adds a constructor method to our VirtualZoo class (shown in bold):

package zoo {
  public class VirtualZoo {
    public function VirtualZoo () {
    }
  }
}

Our application now has an official point of entry. When our application starts, the Flash runtime will automatically create a VirtualZoo instance, executing the VirtualZoo constructor method in the process. Given that our application is a virtual zoo, the first thing we’ll do in the VirtualZoo constructor method is create a VirtualPet object (i.e., add a pet to the zoo). We’ll learn how to create objects next.

Creating Objects

To create an object from a class (known technically as instantiating the object), we use the keyword new in combination with the name of the class. The following generalized code shows the approach:

new ClassName

For example, to make an object from our VirtualPet class, we use the following code:

new VirtualPet

Multiple independent objects can be made from the same class. For example, the following code creates two VirtualPet objects:

new VirtualPet
new VirtualPet

Literal Syntax

We’ve just learned that the generalized syntax for creating a new object is:

new ClassName

That syntax applies to both built-in and custom classes. For example, the following code creates a new instance of the built-in Date class, which represents a particular point in time:

new Date

However, for some native classes, ActionScript also offers an alternative, more convenient means of creating instances, known as literal syntax. For example, to create a new Number instance representing the floating-point number 25.4, we can use the convenient literal form:

25.4

Likewise, to create a new String instance representing the text “hello,” we can use the convenient literal form:

"hello"

Finally, to create a new Boolean instance representing the logical state of true, we can use the convenient literal form:

true

And to create a new Boolean instance representing the logical state of false, we can use the convenient literal form:

false

Literal syntax is also available for the Object, Function, RegExp, and XML classes. We’ll study Object literal syntax in Chapter 15, Function literal syntax in Chapter 5, and XML literal syntax in Chapter 18. For information on RegExp literal syntax, see Adobe’s documentation.

Object Creation Example: Adding a Pet to the Zoo

Now that we know how to create objects, we can add a VirtualPet object to our zoo program. The following code does just that:

package zoo {
  public class VirtualZoo {
    public function VirtualZoo () {
      new VirtualPet
    }
  }
}

Notice that the preceding code refers to the VirtualPet class by its unqualified name, VirtualPet—not by its qualified name, zoo.VirtualPet. Code in a given package can refer to the classes in that package by their unqualified names.

By contrast, code in a given package cannot refer to classes in other packages at all. To gain access to a public class in another package, we use the import directive, which has the following general form:

import packageName.className;

In the preceding code, packageName is the name of the class’s package, and className is the name of the public class we wish to use. If the specified class is not public, the import attempt fails because a non-public class cannot be used outside its package. Once a class has been imported, it can then be referred to by its unqualified name. For example, to create an instance of the built-in flash.media.Sound class (which is used to load and play sounds), we would use the following code:

import flash.media.Sound
new Sound

Importing a class at the package-level makes that class available to code throughout the entire package body. For example, the following code imports flash.media.Sound at the package-level, and then later creates an instance of the Sound class within the VirtualZoo constructor method:

package zoo {
  import flash.media.Sound

  public class VirtualZoo {
    public function VirtualZoo () {
      new Sound
    }
  }
}

If a class’s unqualified name conflicts with the unqualified name of another class, then qualified names must be used to differentiate the two classes. For example, if we were to define a class in the zoo package named Sound, then, within the zoo package, we would use the following code to create an instance of the built-in flash.media.Sound class (notice the use of the qualified name):

new flash.media.Sound

And we would use the following code to create an instance of the zoo package’s Sound class:

new zoo.Sound

Use of the unqualified class name (e.g., Sound) on its own causes an error that prevents the offending program from compiling. Errors that prevent a program from compiling are known as compile-time errors.

To gain access to all the public classes in another package, we use the following code:

import packageName.*

For example, to gain access to all the public classes in the flash.media package, we use the following code:

import flash.media.*

Note that classes contained by a package that has no name are placed in an automatically created package known as the unnamed package. Classes in the unnamed package can be used directly anywhere in a program, without the need for the import directive. In other words:

package {
  // Classes defined here are in the unnamed package, and can be
  // used directly anywhere in a program
}

However, as a best practice, you should avoid defining classes in the unnamed package because their names might conflict with classes (and other kinds of definitions) defined by ActionScript, other programs, or even other parts of the same program.

Tip

On a technical level, the import directive opens the public namespace of the specified package for the current scope and all nested scopes. But if you are new to ActionScript, you needn’t worry about the technical details of the import directive. We’ll examine everything you need to know in the chapters to come.

Now let’s return to the task of creating objects in the virtual zoo program. Recall the following code, which creates a new VirtualPet object (shown in bold):

package zoo {
  public class VirtualZoo {
    public function VirtualZoo () {
      new VirtualPet
    }
  }
}

The preceding code successfully creates a new VirtualPet object, but it also suffers from a problem: after the object has been created, the program has no way to refer to it. As a result, our program cannot subsequently use or control the new pet. To give our program a way to refer to the VirtualPet object, we use variables.

Variables and Values

In ActionScript, every object is considered a single, self-contained piece of data (i.e., information) known as a value. Apart from objects, the only other legal values in ActionScript are the special values null and undefined, which represent the concept of “no value.” A variable is an identifier (i.e., a name) associated with a value. For example, a variable might be the identifier submitBtn associated with an object representing a button in an online form. Or a variable might be the identifier productDescription associated with a String object that describes some product.

Variables are used to keep track of information in a program. They give us a means of referring to an object after it is created.

Variables come in four varieties: local variables, instance variables, dynamic instance variables, and static variables. We’ll study the first two varieties now, and the remaining two varieties later in this book.

Local Variables

Local variables are used to track information temporarily within the physical confines of a constructor method, an instance method, a static method, or a function. We haven’t studied instance methods, static methods, or functions yet so for now we’ll focus on local variables in constructor methods.

To create a local variable within a constructor method, we use a variable definition, as shown in the following generalized code. Notice that the definition starts with the keyword var and, by convention, ends in a semicolon, as do all directives that do not include a block statement. The semicolon indicates the end of the directive, much like the period at the end of a sentence in a natural language.

class SomeClass {
  public function SomeClass () {
    var identifier = value;
  }
}

In the preceding code, identifier is the local variable’s name, and value is the value associated with that variable. Together, the equals sign and the value are known as the variable initializer because they determine the initial value of the variable.

Tip

Associating a variable with a value is known as assigning, setting, or writing the variable’s value.

When the variable initializer is omitted, ActionScript automatically assigns the variable a default value. Default values for variables are discussed in Chapter 8.

A local variable can be used within the method or function that contains its definition only. When the method or function finishes executing, the local variable expires and can no longer be used by the program.

Let’s create a local variable to refer to the VirtualPet object we created earlier in the VirtualZoo constructor. We’ll name the local variable pet, and we’ll use an initializer to associate it with the VirtualPet object. Here’s the code:

package zoo {
  public class VirtualZoo {
    public function VirtualZoo () {
      var pet = new VirtualPet;
    }
  }
}

Having associated the local variable pet with a VirtualPet object, we can now use that variable to refer to, and therefore control, that object. However, currently our VirtualPet object can’t actually do anything because we haven’t programmed its functionality. We’ll start to rectify that shortcoming in the next section by giving pets the ability to have nicknames.

Instance Variables

Earlier we learned that a class describes the characteristics and behavior of a particular type of object. In object-oriented programming terms, a “characteristic” is a specific piece of information (i.e., value) that describes some aspect of an object—such as its width, speed, or color. To keep track of an object’s characteristics, we use instance variables.

An instance variable is a variable attached to a particular object. Typically, each instance variable describes a characteristic of the object to which it is attached. For example, an instance variable might be the identifier width associated with the value 150, describing the size of the button object in an interface. Or, an instance variable might be the identifier shippingAddress associated with the value “34 Somewhere St,” describing the destination of a product-order object.

Instance variables are created using variable definitions directly within class definitions, as shown in the following generalized code:

class SomeClass {
  var identifier = value;
}

Adding an instance variable definition to a class definition causes that variable to be automatically attached to each instance of the class. As with local variables, the initializer of an instance variable definition specifies the initial value of the instance variable. However, because instance variables are set independently for each individual instance of a class, the initial value of an instance variable is very often omitted and assigned later in the program.

As an example, let’s add an instance variable to the VirtualPet class that tracks the nickname of each VirtualPet object. We’ll call our instance variable petName. Here’s the code:

package zoo {
  internal class VirtualPet {
    var petName = "Unnamed Pet";
  }
}

As a result of the preceding code, the instance variable petName is automatically attached to each new instance of the VirtualPet class. The initial value of petName for all VirtualPet instances is “Unnamed Pet.” However, once each VirtualPet instance is created, a new, custom value can be assigned to its petName variable.

To assign an instance variable a new value, we use the following generalized code:

object.instanceVariable = value

In the preceding code, object is the object whose instance variable will be assigned a value, instanceVariable is one of object’s instance variables (as defined by object’s class), and value is the value to assign.

Let’s use the preceding technique to assign a nickname to the VirtualPet object we created earlier in the VirtualZoo constructor. Here’s the code as we last saw it:

package zoo {
  public class VirtualZoo {
    public function VirtualZoo () {
      var pet = new VirtualPet;
    }
  }
}

According to the generalized code for assigning an instance variable a new value, we need to start by referring to an object. In this case, we use the local variable pet to refer to the desired VirtualPet instance:

pet

Next, we write a dot:

pet.

Then, we write the name of the instance variable whose value we wish to assign—in this case, petName:

pet.petName

Finally, we write an equals sign, then the value we wish to assign to the instance variable. Let’s use “Stan”:

pet.petName = "Stan"

Isn’t that cute? Our pet has a name. We’re making progress.

Here’s the code as it appears in our program:

package zoo {
  public class VirtualZoo {
    public function VirtualZoo () {
      var pet = new VirtualPet;
      pet.petName = "Stan";
    }
  }
}

In the preceding code, notice that the petName instance variable, which is defined in the VirtualPet class, is set through a VirtualPet instance from within the VirtualZoo class. The petName instance variable is, therefore, said to be accessible to code in the VirtualZoo class. When a class makes its instance variables accessible to code in other classes, it is conceptually allowing those classes to modify the characteristics of its instances. The nickname of a pet is a characteristic that naturally lends itself to external modification. However, some instance variables represent characteristics that should not be modified outside the class in which they are defined. For example, later in this chapter we’ll create an instance variable, caloriesPerSecond, that represents the speed with which a pet digests its food. If an inappropriately small or large value is assigned to caloriesPerSecond, a pet might starve instantly or never grow hungry. Hence, to prevent external code from assigning an inappropriate value to caloriesPerSecond, we must limit access to that variable. To limit access to a variable, we use access-control modifiers.

Access-control modifiers for instance variables

An instance variable’s access-control modifier controls that variable’s accessibility in a program. The access-control modifiers available for instance variable definitions are public, internal, protected, and private. The public and internal modifiers have the same effect with instance variables that they have with classes: an instance variable declared public can be accessed both inside and outside of the package in which it is defined; an instance variable declared internal can be accessed inside the package in which it is defined only. The protected and private modifiers are even more restrictive than internal. An instance variable declared protected can be accessed by code in the class that contains the variable’s definition, or by code in descendants of that class only (we haven’t studied inheritance yet, so if you are new to object-oriented programming, you can simply ignore protected for now). An instance variable declared private can be accessed by code in the class that contains the variable’s definition only. When no modifier is specified, internal (package-wide access) is used.

Table 1-2 summarizes the access-control modifiers for instance variables.

Table 1-2. Instance variable access-control modifiers
 

Attribute

Code placement

Public

Internal

Protected

Private

Code in class containing variable’s definition

Access allowed

Access allowed

Access allowed

Access allowed

Code in descendant of class containing variable’s definition

Access allowed

Access allowed

Access allowed

Access denied

Code in different class in same package as variable’s definition

Access allowed

Access allowed

Access denied

Access denied

Code not in same package as variable’s definition

Access allowed

Access denied

Access denied

Access denied

By defining a class’s instance variables as private, we can keep each instance’s information safely encapsulated, preventing other code from relying too heavily on the internal structure of the class or accidentally assigning invalid values to instance variables. In general, it’s good form to specify an access-control modifier explicitly for every instance variable. No instance variable should be defined as public unless specifically required by its class’s architecture. If you are unsure which access-control modifier to use, use private. Down the road, you can easily make the instance variable more accessible if required. By contrast, if you start with a public instance variable, you’ll have a tough time changing it to private later if external code already relies on it.

In the current version of our virtual zoo application, the petName instance variable is used within both the VirtualPet class and the VirtualZoo class, so we should define petName with the access-control modifier internal, as follows:

package zoo {
  internal class VirtualPet {
    internal var petName = "Unnamed Pet";
  }
}

Note that defining an instance variable with the internal attribute is identical to defining the variable with no access-control modifier at all (because internal is the default).

There are plenty more examples of instance variables throughout the remainder of this book. Now let’s continue with the development of our virtual zoo program.

So far, the structure of our VirtualPet class requires each VirtualPet object’s petName variable to be set voluntarily. If, however, we want to guarantee that a name is supplied for every pet, we can use constructor parameters, as described in the next section.

Constructor Parameters and Arguments

A constructor parameter is special type of local variable that is created as part of a constructor-method definition. Unlike regular local variables, a constructor parameter’s initial value can be (or in some cases, must be) supplied externally when a new object is instantiated.

Constructor parameters are not created with the keyword var. Instead, to create a constructor parameter, we simply provide the desired name and variable initializer within the parentheses of a constructor function definition, as shown in the following generalized code:

class SomeClass {
  function SomeClass (identifier = value) {
  }
}

In the preceding code, identifier is the name of a constructor parameter, and value is the parameter’s initial value.

To create more than one parameter for a constructor method, we list multiple parameter names, separated by commas, as shown in the following generalized code (notice the line breaks, which are both legal and common):

class SomeClass {
  function SomeClass (identifier1 = value1,
                      identifier2 = value2,
                      identifier3 = value3) {
  }
}

By default, the initial value of a constructor parameter is set to the value supplied in that parameter’s definition. However, a constructor parameter’s value can alternatively be supplied when an object is instantiated, using the following generalized object-creation code:

new SomeClass(value1, value2, value3)

In the preceding code, value1, value2, and value3 are values that are assigned, in order, to the constructor parameters of SomeClass’s constructor method. A value supplied to a constructor parameter when an object is instantiated (as shown in the preceding code) is known as a constructor argument. Using a constructor argument to supply the value of a constructor parameter is known as passing that value to the constructor.

When a constructor parameter definition does not include a variable initializer, that parameter’s initial value must be supplied via a constructor argument. Such a parameter is known as a required constructor parameter. The following generalized code shows how to create a class with a single required constructor parameter (notice that the parameter definition does not include a variable initializer):

class SomeClass {
  function SomeClass (requiredParameter) {
  }
}

Any code that creates an instance of the preceding class must supply requiredParameter’s value using a constructor argument, as shown in the following generalized code:

new SomeClass(value)

Failure to supply a constructor argument for a required parameter causes an error either when the program is compiled (if the program is compiled in strict mode) or when the program runs (if the program is compiled in standard mode). We’ll learn the difference between strict mode and standard mode compilation in Chapter 7.

Tip

When creating a new object without constructor arguments, some programmers choose to retain the constructor-argument parentheses. For example, some programmers prefer to write:

new VirtualPet()

rather than:

new VirtualPet

The choice is entirely stylistic; ActionScript allows both formats. However, the ActionScript programming community favors the former style (with parentheses) over the latter (without parentheses). Hence, from now on, this book will always include parentheses when creating new objects, even when no constructor arguments are used.

Using the preceding generalized parameter code is a guide, let’s add a new constructor method to our VirtualPet class, and define a single, required constructor parameter, name. We’ll use the value of the name parameter to set each VirtualPet object’s petName instance variable. Here’s the basic code for the constructor method, shown, for the moment, without any code in the constructor body:

package zoo {
  internal class VirtualPet {
    internal var petName = "Unnamed Pet";

    public function VirtualPet (name) {
    }
  }
}

Because name is a required parameter, its initial value must be supplied externally at object-creation time. Accordingly, we must update the code that creates our VirtualPet object in the VirtualZoo constructor. Previously, the code looked like this:

package zoo {
  public class VirtualZoo {
    public function VirtualZoo () {
      var pet = new VirtualPet;
      pet.petName = "Stan";
    }
  }
}

Here’s the updated version, which passes the value “Stan” to the VirtualPet constructor instead of assigning it to the new instance’s petName variable:

package zoo {
  public class VirtualZoo {
    public function VirtualZoo () {
      var pet = new VirtualPet("Stan");
    }
  }
}

When the preceding code creates the VirtualPet instance, VirtualPet’s constructor runs, and the constructor argument “Stan” is assigned to the name parameter. Hence, within the VirtualPet constructor, we can use the name parameter to assign the value “Stan” to the new VirtualPet object’s petName instance variable. To do that, we need to specify petName’s value using an identifier expression. The next section describes expressions and identifier expressions.

Expressions

The written form of a value in an ActionScript program is known as an expression. For example, the following code shows a new expression—an expression representing a new object (in this case, a Date object):

new Date()

Likewise, the following code shows a literal expression representing a Number object with the value 2.5:

2.5

Individual expressions can be combined together with operators to create a compound expression, whose value is calculated when the program runs. An operator is a built-in command that combines, manipulates, or transforms values (which are known as the operator’s operands). Each operator is written using either a symbol, such as +, or a keyword, such as instanceof.

For example, the multiplication operator, which multiplies two numbers, is written using the asterisk symbol (*). The following code shows a compound expression that multiplies 4 and 2.5:

4 * 2.5

When the preceding code is executed, ActionScript calculates the result of multiplying 4 by 2.5, and the entire compound expression (4 * 2.5) is replaced by that single calculated result (10). Calculating the value of an expression is known as evaluating the expression.

Tip

For a complete list of ActionScript operators, see Chapter 10.

To represent values that are not known when a program is compiled (at compile-time), but are supplied or calculated when the program runs (i.e., at runtime), we use variable names. When ActionScript evaluates an expression containing a variable name, it replaces that variable name with the corresponding variable’s value. The process of replacing the variable name with the variable’s value is known as retrieving, getting, or reading the variable value.

For example, consider the following compound expression, in which two values represented by variable names are multiplied together:

quantity * price

The variables quantity and price are placeholders for values that will be determined at runtime. The value of quantity might be, say, a number supplied by the user, while the value of price might be a number retrieved from a database. For the sake of this example, let’s assume that the variable quantity has the value 2, and the variable price has the value 4.99.

When ActionScript evaluates the expression quantity * price, it replaces quantity with 2 and price with 4.99. Hence, during evaluation, the expression reads:

2 * 4.99

And the final value of the expression is:

9.98

Tip

In formal terms, an expression that contains a variable name only, such as quantity, is known as an identifier expression.

Now let’s try using an identifier expression in our virtual pet program.

Assigning One Variable’s Value to Another

When we last saw our virtual zoo program, we had just finished creating a constructor method for the VirtualPet class. The constructor method defined a single parameter, name, whose value was supplied externally by object-creation code in the VirtualZoo class. Here’s the code for the VirtualPet and VirtualZoo classes, as we left them:

// VirtualPet class
package zoo {
  internal class VirtualPet {
    internal var petName = "Unnamed Pet";

    public function VirtualPet (name) {
    }
  }
}

// VirtualZoo class
package zoo {
  public class VirtualZoo {
    public function VirtualZoo () {
      var pet = new VirtualPet("Stan");
    }
  }
}

Now that we know how to use variables in expressions, we can use the name parameter to assign the value “Stan” to the new VirtualPet object’s petName instance variable.

Recall that to assign an instance variable a new value, we use the following generalized code:

object.instanceVariable = value

According to that generalized code, we need to start our variable assignment by referring to an object. In this case, that object is the new VirtualPet instance being created. To refer to it, we use the keyword this, which is an automatically created parameter whose value is the object being created:

this

Tip

Within the body of a constructor method, the object being created is known as the current object. To refer to the current object, we use the keyword this.

After the keyword this, we write a dot, followed by the name of the instance variable whose value we wish to assign—in this case petName.

this.petName

Finally, we write an equals sign, then the value we wish to assign to the instance variable:

this.petName = value

The value we wish to assign is the value associated with the name parameter. Hence, for value, we write simply: name.

this.petName = name

At runtime, ActionScript replaces name, in the preceding code, with the value passed to the VirtualPet constructor. That value is then assigned to the instance variable petName.

Here’s the assignment code as it appears in our VirtualPet constructor:

package zoo {
  internal class VirtualPet {
    internal var petName = "Unnamed Pet";

    public function VirtualPet (name) {
      this.petName = name;
    }
  }
}

Now that petName’s value is assigned in the VirtualPet constructor, we can remove the redundant initial value “Unnamed Pet” in the petName variable definition. The petName variable definition used to look like this:

internal var petName = "Unnamed Pet";

From now on, it will look like this (notice the removal of the variable initializer):

package zoo {
  internal class VirtualPet {
    internal var petName;

    public function VirtualPet (name) {
      this.petName = name;
    }
  }
}

Tip

An expression that assigns a variable a value, such as this.petName = name is known as an assignment expression. The equals sign in assignment expressions is an operator called the assignment operator.

Copies and References

In the preceding section, we learned how to assign one variable’s value to another. Specifically, we assigned the value of the parameter name to the instance variable petName. Here’s the code:

this.petName = name;

The result of assigning the one variable’s value to another variable depends on the type of value being assigned.

In an assignment where the source variable’s value is an instance of String, Boolean, Number, int, or uint, ActionScript makes a copy of that value and assigns the copy to the destination variable. After the assignment, two separate copies of the original value exist in system memory—the original value itself, and the copy of that value. The source variable points, or refers, to the original value in memory. The destination variable refers to the new value in memory.

By contrast, in an assignment where the source variable’s value is an instance of a custom class or an instance of a built-in class other than String, Boolean, Number, int, or uint, ActionScript associates the second variable directly with the first variable’s value. After the assignment, only one copy of the value exists in memory, and both variables refer to it. The variables are said to share a reference to the single object in memory. As a natural consequence, changes to the object made through the first variable are reflected by the second variable. For example, consider the following code, which creates two local variables, a and b, and then assigns a’s value to b:

var a = new VirtualPet("Stan");
var b = a;

When the first line of the preceding code runs, ActionScript creates a new VirtualPet object, stores that object in memory, and then associates the local variable a with that object. When the second line of the preceding code runs, ActionScript associates the local variable b with the VirtualPet object already referred to by a. Changes made to the VirtualPet object through a are, hence, naturally reflected by b, and vice versa. For example, if we assign petName using the code b.petName = "Tom", then subsequently retrieving a.petName also yields “Tom.” Or, if we assign petName using the code a.petName = "Ken", then subsequently retrieving b.petName also yields “Ken.”

Tip

A variable associated with an object does not store or contain that object—it simply refers to that object. The object, itself, is stored internally by ActionScript, in system memory.

An Instance Variable for Our Pet

Earlier, we learned that a local variable expires when the method or function in which it is defined finishes executing. To make sure that the VirtualPet instance in our VirtualZoo class will be accessible after the VirtualZoo constructor finishes, let’s update the VirtualZoo class. Instead of assigning our VirtualPet object to a local variable, we’ll assign it to an instance variable, pet. We’ll make pet private so that it can be accessed by code in the VirtualZoo class only. Here’s the code (the new instance variable is shown in bold):

package zoo {
  public class VirtualZoo {
    private var pet;

    public function VirtualZoo () {
      this.pet = new VirtualPet("Stan");
    }
  }
}

Over the preceding several sections, we’ve learned how to use instance variables to give characteristics to the objects of a class. Now let’s explore how to use instance methods to give behaviors to the objects of a class.

Instance Methods

An instance method is a discrete set of instructions that carry out some task related to a given object. Conceptually, instance methods define the things an object can do. For example, the built-in Sound class (whose instances represent sounds in a program) defines an instance method named play that can start a sound playing. Likewise, the built-in TextField class (whose instances represent onscreen text) defines a method named setSelection that can change the amount of text selected in the text field.

To create an instance method, we use a function definition within a class block, as shown in the following generalized code:

class SomeClass {
  function identifier () {
  }
}

In the preceding code, the keyword function begins the instance method. Next comes the instance method name, which can be any legal identifier. (Recall that identifiers must not contain spaces or dashes, and cannot start with a number.) The method name is followed by a pair of parentheses that contain a list of method parameters, which we’ll study later. The curly braces ({}) following the parameter list are a block statement. A instance method’s block statement is known as the method body. The method body contains directives that perform some task.

Tip

Instance methods are created using the function keyword because they are, technically speaking, a type of function. We’ll study functions in Chapter 5.

To execute the code in a given method body, we use a call expression, as shown in the following generalized code. Notice the important and mandatory use of the parentheses operator, ( ), following the method name.

object.methodName()

In the preceding code, methodName is the name of the method whose code should be executed, and object is a reference to the specific instance that will conceptually perform the task represented by the specified method. Using a call expression to execute the code in an instance method’s body is known as calling a method of an object (or, synonymously calling a method through an object, or calling an object’s method). The term invoke is also used to mean call.

Tip

When discussing a particular method by name, most documentation includes the parentheses operator, ( ). For example, typical documentation would write setSelection( ) rather than setSelection. The convention of including the parentheses operator helps distinguish method names from variable names in prose. To further emphasize the distinction between variable names and method names, this book italicizes method names and uses constant-width font for variable names.

Let’s put the preceding concepts into practice in our virtual zoo program.

To give our pets the ability to eat, we’ll add a new instance variable and a new instance method to the VirtualPet class. The new instance variable, currentCalories, will track the amount of food each pet has eaten, as a numeric value. The new instance method, eat( ), will implement the concept of eating by adding 100 calories to currentCalories. Eventually, the eat( ) method will be called in response to a user action—feeding a pet.

The following code shows the currentCalories variable definition. To prevent external code from tampering with the amount of calories each VirtualPet instance has, we define currentCalories as private. Notice that each new VirtualPet instance is given 1,000 calories to start:

package zoo {
  internal class VirtualPet {
    internal var petName;
    private var currentCalories = 1000;

    public function VirtualPet (name) {
      this.petName = name;
    }
  }
}

The following code shows the basic eat( ) method definition. Notice that, by convention, instance methods are listed after the class’s constructor method, while instance variables are listed before the class’s constructor method.

package zoo {
  internal class VirtualPet {
    internal var petName;
    private var currentCalories = 1000;

    public function VirtualPet (name) {
      this.petName = name;
    }

    function eat () {
    }
  }
}

Even though the eat( ) method body does not yet contain any code, with the preceding definition in place, we can already invoke the eat( ) method on a VirtualPet object, as shown in the following updated version of the VirtualZoo class:

package zoo {
  public class VirtualZoo {
    private var pet;

    public function VirtualZoo () {
      this.pet = new VirtualPet("Stan");
      // Invoke eat() on the VirtualPet object referenced by the
      // variable pet
      this.pet.eat();
    }
  }
}

Within the eat( ) method body, we want to add 100 to the currentCalories variable of the object through which the eat( ) method was called. To refer to that object, we use the keyword this.

Tip

Within the body of an instance method, the object through which the method is called is known as the current object. To refer to the current object, we use the keyword this. Notice that the term “current object” can refer to either the object being created in a constructor method or the object through which an instance method was called.

Adding a numeric value (such as 100) to an existing variable (such as currentCalories) is a two-step process. First, we calculate the sum of the variable and the numeric value; then we assign that sum to the variable. Here’s the generalized code:

someVariable = someVariable + numericValue

In the case of the eat( ) method, we want to add 100 to the currentCalories variable of the current object (this). Hence, the code is:

this.currentCalories = this.currentCalories + 100;

As a convenient alternative to the preceding code, ActionScript offers the addition assignment operator, +=, which, when used with numbers, adds the value on the right to the variable on the left, as shown in the following code:

this.currentCalories += 100;

Here’s the code as it appears in the VirtualPet class:

package zoo {
  internal class VirtualPet {
    internal var petName;
    private var currentCalories = 1000;

    public function VirtualPet (name) {
      this.petName = name;
    }

    function eat () {
      this.currentCalories += 100;
    }
  }
}

From now on, every time a VirtualPet instance’s eat( ) method is called, that instance’s currentCalories variable will increase by 100. For example, the following code, repeated from the VirtualZoo constructor, increases pet’s currentCalories to 1,100 (because all VirtualPet instances start with 1,000 calories).

this.pet = new VirtualPet("Stan");
this.pet.eat();

Notice that even though the VirtualPet characteristic currentCalories is kept private, it can still be modified as result of a VirtualPet instance performing a behavior (eating) that is instigated by an external code. In some cases, however, even instance methods must be kept private. As with instance variables, we use access-control modifiers to control the accessibility of instance methods in a program.

Access Control Modifiers for Instance Methods

The access-control modifiers available for instance method definitions are identical to those available for instance variables—public, internal, protected, and private. An instance method declared public can be accessed both inside and outside of the package in which it is defined; an instance method declared internal can be accessed only inside the package in which it is defined. An instance method declared protected can be accessed by code in the class that contains the method’s definition, or by code in descendants of that class only (we haven’t studied inheritance yet, so if you are new to object-oriented programming, you can simply ignore protected for now). An instance method declared private can be accessed by code in the class that contains the method’s definition only. When no modifier is specified, internal (package-wide access) is used.

By adding access-control modifiers to the methods of the class, we can put the “black box” principle into strict practice. In object-oriented programming, each object can be thought of as a black box that is controlled by an external assortment of metaphoric knobs. The object’s internal operations are unknown (and unimportant) to the person using those knobs; all that matters is that the object performs the desired action. An object’s public instance methods are the knobs by which any programmer can tell that object to perform some operation. An object’s non-public methods perform other internal operations. Hence, the only methods a class should make publicly accessible are those that external code needs when instructing instances of that class to do something. Methods needed to carry out internal operations should be defined as private, protected, or internal. As an analogy, think of an object as a car, whose driver is the programmer using the object, and whose manufacturer is the programmer that created the object’s class. To drive the car, the driver doesn’t need to know how a car’s engine works. The driver simply uses the gas pedal to accelerate and the steering wheel to turn. Accelerating the car in response to the driver stepping on the gas pedal is the manufacturer’s concern, not the driver’s.

As you manufacture your own classes, focus as much energy designing the way the class is used as you do implementing how it works internally. Remember to put yourself in the “driver’s seat” regularly. Ideally, the way the class’s public methods are used externally should change very little or not at all each time you make an internal change to the class. If you put a new engine in the car, the driver should still be able to use the gas pedal. As much as possible, keep the volatility of your classes behind the scenes, in private methods.

Tip

In object-oriented terms, a class’s public instance methods and public instance variables are, together, sometimes called the class’s interface to the outside world—or, synonymously, the class’s API (Application Programming Interface).

The term API also refers to the collective services provided by an entire group of classes. For example, the built-in Flash-runtime classes for displaying content on screen are known as the display API. Likewise, a custom set of classes used to render 3D content might be known as a “3D API”. In addition to classes, APIs can also include other program definitions (such as variables and functions).

In ActionScript, the term interface has an additional technical meaning, covered in Chapter 9. To avoid confusion, this book does not use the term “interface” to describe an object’s public instance methods and public instance variables.

Returning to our virtual zoo program, let’s now add an access-control modifier to the VirtualPet class’s eat( ) method. We’ll make eat( ) a public method because it is one of the official means by which external code is intended to control VirtualPet objects. Here’s the revised code:

package zoo {
  internal class VirtualPet {
    internal var petName;
    private var currentCalories = 1000;

    public function VirtualPet (name) {
      this.petName = name;
    }

    public function eat () {
      this.currentCalories += 100;
    }
  }
}

As it stands, the VirtualPet class’s eat( ) method is inflexible because it adds the same amount of calories to currentCalories every time it is called. Eventually, we’ll want to dynamically adjust the amount of calories added when a pet eats based on the type of food fed to it by the user. To allow the amount of calories added at feeding time to be specified externally when eat( ) is called, we need method parameters.

Method Parameters and Arguments

Like constructor parameters, a method parameter is special type of local variable that is created as part of a method definition, but whose initial value can be (or, in some cases, must be) supplied externally when the method is called.

To define a method parameter, we use the following generalized code. Notice that a method-parameter definition has the same general structure as a constructor-parameter definition.

function methodName (identifier1 = value1,
                     identifier2 = value2,
                     ...
                     identifiern = valuen) {
}

In the preceding code, identifier1=value1,identifier2=value2,...identifiern=valuen is a list of method parameter names and their corresponding initial values. By default, a method parameter’s initial value is the value supplied in that parameter’s definition. However, a method parameter’s value can alternatively be supplied via a call expression, as shown in the following generalized code:

theMethod(value1, value2,...valuen)

In the preceding code, theMethod is a reference to the method being invoked, and value1, value2,...valuen is a list of values that are assigned, in order, to theMethod’s parameters. A value supplied to a method parameter through a call expression (as shown in the preceding code) is known as a method argument. Using a method argument to supply the value of a method parameter is known as passing that value to the method.

As with constructor parameters, when a method parameter definition does not include a variable initializer, that parameter’s initial value must be supplied via a method argument. Such a parameter is known as a required method parameter. The following generalized code shows how to create a method with a single required method parameter (notice that the parameter definition does not include a variable initializer):

function methodName (requiredParameter) {
}

Any code that calls the preceding method must supply requiredParameter’s value using a method argument, as shown in the following generalized code:

theMethod(value)

Failure to supply a method argument for a required parameter causes an error either when the program is compiled (if the program is compiled in strict mode) or when the program runs (if the program is compiled in standard mode).

Now let’s update the VirtualPet class’s eat( ) method to include a required parameter, numberOfCalories. Each time eat( ) is called, we’ll increase the value of the current object’s currentCalories variable by the value of numberOfCalories. Here’s the updated code for the eat( ) method:

package zoo {
  internal class VirtualPet {
    internal var petName;
    private var currentCalories = 1000;

    public function VirtualPet (name) {
      this.petName = name;
    }

    public function eat (numberOfCalories) {
      this.currentCalories += numberOfCalories;
    }
  }
}

Because numberOfCalories is a required parameter, its initial value must be supplied externally when eat( ) is called. Let’s try it out with the VirtualPet object created in the VirtualZoo constructor. Previously, the code for the VirtualZoo constructor looked like this:

package zoo {
  public class VirtualZoo {
    private var pet;

    public function VirtualZoo () {
      this.pet = new VirtualPet("Stan");
      this.pet.eat();
    }
  }
}

Here’s the updated version, which passes the value 50 to eat( ):

package zoo {
  public class VirtualZoo {
    private var pet;

    public function VirtualZoo () {
      this.pet = new VirtualPet("Stan");
      this.pet.eat(50);
    }
  }
}

The preceding call expression causes eat( ) to run with the value 50 assigned to the numberOfCalories parameter. As a result, 50 is added to the currentCalories instance variable of the VirtualPet instance referenced by pet. After the code completes, the value of pet’s currentCalories variable is 1050.

Method Return Values

Just as methods can accept values in the form of arguments, methods can also produce or return values. To return a value from a method, we use a return statement, as shown in the following general code:

function methodName () {
  return value;
}

Tip

The value returned by a method is known as the method’s return value or result.

When a method executes, its return value becomes the value of the call expression that called it.

To demonstrate the use of method return values, let’s add a new method to the VirtualPet class that calculates and then returns the age of a pet. In order to be able to calculate a pet’s age, we need a little knowledge of the Date class, whose instances represent specific points in time. To create a new Date instance, we use the following code:

new Date()

Times represented by Date instances are expressed as the “number of milliseconds before or after midnight of January 1, 1970.” For example, the time “one second after midnight January 1, 1970” is expressed by the number 1000. Likewise, the time “midnight January 2, 1970” is expressed by the number 86400000 (one day is 1000 milliseconds × 60 seconds × 60 minutes × 24 hours). By default, a new Date object represents the current time on the local system.

To access a given Date instance’s numeric “milliseconds-from-1970” value, we use the instance variable time. For example, the following code creates a new Date instance and then retrieves the value of its time variable:

new Date().time;

On January 24, 2007, at 5:20 p.m., the preceding code yielded the value: 1169677183875, which is the precise number of milliseconds between midnight January 1, 1970 and 5:20 p.m. on January 24, 2007.

Now let’s return to the VirtualPet class. To be able to calculate the age of VirtualPet objects, we must record the current time when each VirtualPet object is created. To record each VirtualPet object’s creation time, we create an instance of the built-in Date class within the VirtualPet constructor, and then assign that instance to a VirtualPet instance variable, creationTime. Here’s the code:

package zoo {
  internal class VirtualPet {
    internal var petName;
    private var currentCalories = 1000;
    private var creationTime;

    public function VirtualPet (name) {
      this.creationTime = new Date();
      this.petName = name;
    }

    public function eat (numberOfCalories) {
      this.currentCalories += numberOfCalories;
    }
  }
}

Using creationTime, we can calculate any VirtualPet object’s age by subtracting the object’s creation time from the current time. We’ll perform that calculation in a new method named getAge( ). Here’s the code:

public function getAge () {
  var currentTime = new Date();
  var age = currentTime.time - this.creationTime.time;
}

To return the calculated age, we use the following return statement:

public function getAge () {
  var currentTime = new Date();
  var age = currentTime.time - this.creationTime.time;

  return age;
}

The following code shows the getAge( ) method in the context of the VirtualPet class:

package zoo {
  internal class VirtualPet {
    internal var petName;
    private var currentCalories = 1000;
    private var creationTime;

    public function VirtualPet (name) {
      this.creationTime = new Date();
      this.petName = name;
    }

    public function eat (numberOfCalories) {
      this.currentCalories += numberOfCalories;
    }

    public function getAge () {
      var currentTime = new Date();
      var age = currentTime.time - this.creationTime.time;
      return age;
    }
  }
}

Now let’s use getAge( )’s return value in the VirtualZoo class. Consider the getAge( ) call expression in the following updated version of VirtualZoo:

package zoo {
  public class VirtualZoo {
    private var pet;

    public function VirtualZoo () {
      this.pet = new VirtualPet("Stan");
      this.pet.getAge();
    }
  }
}

In the preceding code, the expression pet.getAge( ) has a numeric value representing the number of milliseconds since the creation of the VirtualPet object referenced by pet. In order to be able to access that value later in the program, we could assign it to a variable, as follows:

package zoo {
  public class VirtualZoo {
    private var pet;

    public function VirtualZoo () {
      this.pet = new VirtualPet("Stan");
      var age = this.pet.getAge();
    }
  }
}

Alternatively, in a more complete version of the virtual zoo program, we might display the returned age on screen for the user to see.

Method return values are a highly common part of object-oriented programming. We’ll use them extensively throughout this book, as you will in your own code.

Note that like any other expression, a call expression can be combined with other expressions using operators. For example, the following code uses the division operator to calculate half the age of a pet:

pet.getAge() / 2

Likewise, the following code creates two VirtualPet objects, adds their ages together, and assigns the sum to a local variable, totalAge:

package zoo {
  public class VirtualZoo {
    private var pet1;
    private var pet2;

    public function VirtualZoo () {
      this.pet1 = new VirtualPet("Sarah");
      this.pet2 = new VirtualPet("Lois");
      var totalAge = this.pet1.getAge() + this.pet2.getAge();
    }
  }
}

Note that when a return statement does not include any value to return, it simply terminates the currently executing method. For example:

public function someMethod () {
  // Code here (before the return statement) will be executed

  return;

  // Code here (after the return statement) will not be executed
}

The value of a call expression that calls a method with no return value (or with no return statement at all) is the special value undefined. Return statements with no return value are typically used to terminate methods based on some condition.

Method Signatures

In documentation and discussions of object-oriented programming, a method’s name and parameter list are sometimes referred to as the method’s signature. In ActionScript, a method signature also includes each parameter’s datatype and the method’s return type. Parameter datatypes and method return types are discussed in Chapter 8.

For example, the signature of the eat( ) method is:

eat(numberOfCalories)

The signature of the getAge( ) method is simply:

getAge()

We’ve now covered the basics of instance methods. Before we conclude this chapter, we’ll study one last issue related to ActionScript vocabulary.

Members and Properties

In the ActionScript 3.0 specification, an object’s variables and methods are referred to collectively as its properties, where property means “a name associated with a value or method.” Confusingly, in other ActionScript documentation (most notably Adobe’s ActionScript Language Reference), the term property is also used to mean “instance variable.” To avoid the confusion caused by this contradiction, this book avoids the use of the term “property” entirely.

Where necessary, this book uses the traditional object-oriented programming term instance members (or simply members) to refer to a class’s instance methods and instance variables collectively. For example, we might say "radius is not a member of Box,” meaning that the Box class does not define any methods or variables named radius.

Virtual Zoo Review

This chapter has introduced a large number of concepts and terms. Let’s practice using them by reviewing our virtual zoo program for the last time in this chapter.

Our virtual zoo game has two classes: VirtualZoo (the main class) and VirtualPet (which represents the pets in the zoo).

When our program starts, the Flash runtime automatically creates an instance of VirtualZoo (because VirtualZoo is the application’s main class). The act of creating the VirtualZoo instance causes the VirtualZoo constructor method to execute. The VirtualZoo constructor method creates an instance of the VirtualPet class, with a single constructor argument, “Stan.”

The VirtualPet class defines three instance variables, petName, currentCalories, and creationTime. Those three instance variables represent the following pet characteristics: the pet’s nickname, the amount of food in the pet’s stomach, and the pet’s birth date. For a new VirtualPet object, the initial value of currentCalories is a number created using the literal expression 1000. The initial value of creationTime is a Date object representing the time at which each VirtualPet object is created. When a VirtualPet object is created, petName is assigned the value of the required constructor parameter, name. The constructor parameter name receives its value through a constructor argument, supplied by the new expression that creates the VirtualPet object.

The VirtualPet class defines two instance methods, eat( ) and getAge( ). The eat( ) method increases currentCalories by the specified numeric value. The getAge( ) method calculates and returns the pet’s current age, in milliseconds.

Example 1-2 displays the current code for our zoo program.

Example 1-2. Zoo program
// VirtualPet class
package zoo {
  internal class VirtualPet {
    internal var petName;
    private var currentCalories = 1000;
    private var creationTime;

    public function VirtualPet (name) {
      this.creationTime = new Date();
      this.petName = name;
    }

    public function eat (numberOfCalories) {
      this.currentCalories += numberOfCalories;
    }

    public function getAge () {
      var currentTime = new Date();
      var age = currentTime.time - this.creationTime.time;
      return age;
    }
  }
}

// VirtualZoo class
package zoo {
  public class VirtualZoo {
    private var pet;

    public function VirtualZoo () {
      this.pet = new VirtualPet("Stan");
    }
  }
}

Break Time!

We’ve made great progress in this chapter. There’s lots more to learn, but it’s time for a well-deserved break. When you’re ready for more ActionScript 3.0 essentials, head on to the next chapter.

Get Essential ActionScript 3.0 now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.