67% Complete
Star

Light
-

Chapter 5
80/20
JavaScript

Have you ever heard of the Pareto principle? It states that roughly 80% of the output from a system results from 20% of the input. Though this is not a universal rule for all systems, it appears often enough to be a respected truth.

The aim of this chapter is to introduce JavaScript with this 80/20 notion in mind. Though both numbers are rough, the takeaway is that you can be productive in JavaScript by knowing and using a small and specific subset of the language. This approach is in contrast to knowing all its keywords, their behavior, syntax, and the various subsystems. I recommend learning them later if you are interested, but you don't need them all to be productive.

The subset we'll focus on will help you author code that works. As you saw in Work. Right. Better., this focus does not mean the code is not right. The mantra simply provides a path for improving code over time.

Prior to introducing this subset and detailing its parts we'll briefly cover JavaScript in terms of the environment in which it is executed. We will then cover a mindset that explains useful tactics for reading and authoring JavaScript. Finally, we'll be ready to dive into the subset, the code itself.

#

Environment

As mentioned in the Event Loop section, there is a runtime that JavaScript is executed within. This runtime is also known as the runtime environment. The browser has been the runtime environment of focus thus far. This fact will remain. You should know however that JavaScript can be executed in different environments.

In the Client and Server section, we learned that the browser is a client. Can JavaScript also be executed in a server environment? You bet. Some programs even embed an environment enabling coders to author plug-ins (new software) that extend the original program. JavaScript is a very flexible programming language and for this reason it is useful in many environments.

Though it may seem odd at first, each environment expects a particular language version. This idea makes a lot of sense in programming unlike English for example. A version consists of a specific set of keywords, syntax, and functionality. This allows a language to evolve where changes to it won't break existing programs. A language evolves and can improve when third-party and custom code is so useful that it should be built-in.

We will continue to focus on JavaScript in the browser, but just know it has more than one home and that its home expects a particular language version. The code in this book is version six (ES6). It's worth noting that the subset in this book makes the code look like version five (ES5). This is intentional. The majority of ES6 additions fall in the right and better categories for advanced coders, not beginners. This will likely be the case for all future versions in fact.

#

Mindset

The five notions below will influence your way of thinking about JavaScript throughout the rest of this book and beyond. They will simultaneously explain useful tactics for reading and authoring code. For brevity, I will use the term coding to encapsulate reading and authoring moving forward. The five notions are:

  1. Thinking in three zoom levels
  2. Functions are your friend
  3. Prototype to learn
  4. Don't repeat yourself
  5. Refactor early and often

The first, thinking in three zoom levels will help you determine what to focus on at a given time. Functions are your friend will highlight why functions are so paramount. Prototype to learn, don't repeat yourself, and refactor early and often are programming mantras found in The Pragmatic Programmer by Andrew Hunt and David Thomas. They encourage you to experiment, author reusable code, and aggressively refine it. Combined, the five notions will influence your actions while coding.

1. Thinking in Three Zoom Levels

There are three zoom levels you should consider when coding:

  1. Scope tree
  2. Statement pattern
  3. Value resolution

These zoom levels are useful at authoring time (within code files) and execution time (within the program). This three zoom level approach is a tactic for navigating and understanding code more quickly.

It is worth noting that any text editor is usable for coding. However, dedicated code editors are designed specifically to make our coding efforts less painful and more efficient. Ask online or local programmers that you trust to determine which code editor is best for you. SublimeText, Visual Studio Code, and WebStorm (among many others) are all great editors currently.

Zoom Level 1 - Scope Tree

Whenever you are starting to read or author code, place yourself at zoom level 1. Take note that your editor, authoring environment, and debugger (we'll cover this guy later) may each be of help at this step.

When reading, the goal is to scan for functions. You are looking at both their names and their nested structure. JavaScript programs are simply a tree of functions and thus a tree of scopes. Identifying the names and nesting of functions helps you understand the structural shape of the program. Grasping the program shape (or a subset of the program shape) helps you understand where you want to zoom in next.

When authoring, the goal is the same. If starting with no code you'll lack program shape, so this is only important when adding new code. When code already exists, this effort determines the target location for the code you plan to add. It can be difficult to select the location correctly the first time so don't stress. This fact is why we'll explore the refactor early and often mantra soon.

Grasping the function names and their nested structure (program shape) is vital. We will cover their three fundamental use cases in the Functions section that soon follows. Once a particular function is of enough interest, you enter zoom level 2.

Nested Functions Highlight Example

#Nested Functions Highlight Example

Zoom Level 2 - Statement Pattern

Zoom level 2 is all about prepping yourself to get answers in zoom level 3. Answers in this context are synonymous with values. Once you have values, they can be understood and operated on. Values ensure a function can actually do work.

We will cover the specific statement patterns to look out for later in the Statements section.

Statement Pattern Highlight Example

#Statement Pattern Highlight Example

Zoom Level 3 - Value Resolution

At zoom level 3 we have determined the statement pattern for a given code statement. Once determined, this informs the steps we take in resolving the values of it. Again, functions can't do work unless values exist. Since a program is a dynamic and living thing, these values can be different at different times. We take the three zoom level approach to help us determine the exact values at a given time.

The takeaway is that the three zoom levels help you navigate and understand code more quickly.

Value Resolution Highlight Example

#Value Resolution Highlight Example

2. Functions are your Friend

We will continue to reinforce the importance of functions throughout this book. They enable interactive code and the respective manifestation of the amazing games, tools, and software we love, to exist. It is in your best interest to make functions your friend.

Each function has one of three fundamental use cases in JavaScript. Remember that a function still encapsulates its own scope regardless of use case. Each use case provides a means to a desired end. These use cases are:

  • Function as organizational unit
  • Function as instantiatable unit
  • Function as reusable work unit

The takeaway is that functions are paramount to a program. Without them, a program is of no use. We'll cover these three use cases in great detail in the Functions section below.

3. Prototype to Learn

Code is virtual not physical. We must use this reality to our advantage. Authoring code and throwing it away is cheap and easy. We can undo and redo in an instant. This quality enables us to iterate quickly toward a solution or desired result with minimal consequence.

Cheap and easy is not to be confused with being invaluable however. Prototyping is extremely valuable as you can explore a solution space quickly to learn. Learning through quick iteration eventually manifests as a clear path to follow. Following this path either leads to a solution or surfaces a new idea to prototype against.

The takeaway is that prototyping is how we sketch and experiment when coding. Taking shortcuts and ignoring best practices is OK here. Only after we have a working prototype are we concerned with not taking shortcuts and following best practices. Prototypes require minimal effort but result in great value. Completing a prototype ensures we have something that works. Right and better are for later.

4. Don't Repeat Yourself

Programming enables us to break the restrictions of the physical world. Every single day we do work to get results. Often times we've done this work thousands of times before. A few things I do everyday that fit this description include eating, drinking water, and reading. In each example I use energy to accomplish work in an effort to get a result. The work takes some amount of time before I get the result. The result is not instantaneous. Regardless of how good or fast I get at the tasks that comprise the work, the result will never be instantaneous.

We lack this restriction in the virtual world. We can get results instantly. In software we can encode the work. Once encoded we can simply execute a particular function to do work and to get a desired result. The same effort that was initially required no longer exists. It is encoded. Electricity is so fast the result of the work seems instantaneous.

The takeaway is that we strive to create functions that are reusable. We briefly covered this idea earlier in the Elements and Elements section. Additionally, we provided a concrete example in the Sync and Async section with the changeBackgroundColor(newColor) function. We will further explore this idea and more concrete examples in the Functions section.

5. Refactor Early and Often

As previously mentioned, it can be difficult to select the correct location when first adding code. Additionally, it can be difficult enough to get code to work through prototyping. It can even be difficult to name identifiers well (believe it or not this is one of the more difficult aspects of programming). Thankfully the code is not set in stone. It is easy to move, change, and rename. This is refactoring. We are working in the virtual not physical world after all. Use this to your advantage. Our editors can even help us accomplish this faster while minimizing error.

The core benefit of refactoring is that it enables us to improve code readability. Remember the three zoom levels? Refactoring helps us here. Code complexity can also be reduced which makes it more understandable to us, other coders, and our future self.

Take note that refactoring requires that the correct work still gets done. In other words, the functional behavior remains where the implementation of that behavior may differ. This idea parallels the Work. Right. Better. mantra in that refactoring leads to right and better.

It is worth noting however that refactoring is a susceptible step in that it can lead to bugs. A bug is code that unintentionally prevents work or that does work incorrectly. We'll cover this more and explore concrete examples in the Errors and Debugging sections.

The takeaway is that code is a living thing. It can be molded into a more perfect shape. Code can be made more understandable during authoring time while becoming more efficient during execution time. By refactoring early and often we can author more readable, less complex, and more efficient code.

#

Subset

Just like natural languages have many words, rules, and exceptions, so too do programming languages. As we all know from experience, we only use a small fraction of English to communicate. JavaScript—and programming languages in general—are no different.

The question is, why do extra words in a language even exist? Extra words are useful to those more experienced with a given language. They enable concise communication between those in-the-know. They are intended as a shortcut to shared meaning and understanding. The tradeoff of using these words is a risk of increased misunderstanding for those unfamiliar with them. The subset approach mitigates this risk.

The JavaScript subset—and the language itself—is organized in four groups:

  1. Keywords
  2. Expressions
  3. Operators
  4. Statements

These four groupings make a program useful by enabling it to do work during execution time. Think of the respective groupings as:

  1. Named shortcuts to values
  2. Values
  3. Special characters for changing values
  4. Patterns of special characters and keywords

Keywords are named shortcuts to values. They enable us to use a natural-language-like key for identifying something as meaningful.

Expressions are values. They unsurprisingly can be represented with keywords, but also with literals (like the number 360) or to-be-expressed evaluations (which we'll cover later). Without values, we would not be able to translate meaningful ideas to a computer.

Operators are special characters for changing values. Operators enable values to be operated on and changed. You already know about the arithmetic operators +, -, *, and / from math class.

Statements are patterns of special characters and keywords. They enable us to group and reason about the various special characters, keywords, values, and operators in a particular portion of code using a small pattern. Put another way, we state something meaningful using a small pattern.

Combined, all four groups enable us to author code that a JavaScript engine understands. When this happens in the browser, it works with other programs on the computer to ensure we see our animated and interactive creations on screen. Let's dig into each group one-by-one.

#

Keywords

So we now know keywords are named shortcuts to values. Each keyword is a key used to access a value using a natural-language-like word. Keywords are not always full words as you would expect in English. They are sometimes abbreviations (like var is for variable) or aggregations (like makeBackgroundBlack is for "make background black"). The former is a reserved keyword where the latter is a non-reserved keyword. These are the two keyword types in JavaScript:

  1. Reserved keywords
  2. Non-reserved keywords

Put another way, reserved keywords are those that cannot be reassigned a value. They have a predefined value assigned by JavaScript. This value cannot be changed. In contrast, non-reserved keywords can be reassigned a value. They also have a predefined value by default, but it can be changed.

Non-reserved keywords are also known as identifiers. An identifier is simply any keyword that isn't already reserved. Non-reserved keywords are organized in three groups:

  1. JavaScript identifiers
  2. Environment identifiers
  3. Custom identifiers

A JavaScript identifier is a keyword with a predetermined value. This value is useful to your code as it can help facilitate language-specific work. An environment identifier is a keyword with a predetermined value also. Its value is useful to your code as it can help facilitate environment-specific work. Custom identifiers have the special JavaScript value known as undefined until the identifier is reassigned a value. So JavaScript identifiers and environment identifiers each have predetermined values. These values are set by the language and environment respectively. Custom identifiers have the predetermined value undefined until we reassign them a value (this is an example of the assignment statement pattern we'll learn about later).

Let's explore what we just learned relative to a familiar code snippet:

function makeBackgroundBlack() {
  document.body.style.backgroundColor = '#000000';
}

makeBackgroundBlack();

Before reading on, test yourself by listing out what you think the keywords are. Then try to guess each keyword's reserved or non-reserved status (this will be challenging I know).

Answer time:

  1. function - reserved
  2. makeBackgroundBlack - custom identifier
  3. document - environment identifier
  4. body - environment identifier
  5. style - environment identifier
  6. backgroundColor - environment identifier

You may have thought '#000000' was a keyword, but it is a literal value. We'll cover the distinction between literal values and keywords in the next section.

The takeaway is that any portion of code that resembles a natural-language-like word (abbreviations and aggregations included) is a keyword. Everything else is either a literal value, an operator, or a statement's special character(s).

Reserved Keywords

There is one reserved keyword in the snippet above. There are over forty in JavaScript. With our subset approach there are ten that we care about. Listed alphabetically they are:

  1. debugger
  2. else
  3. false - (also a literal value)
  4. function
  5. if
  6. new
  7. null - (also a literal value)
  8. return
  9. true - (also a literal value)
  10. var

These are the reserved keywords to get really familiar with. We'll explore them in more detail in the remaining sections of this chapter in addition to the Deconstructing Designs chapter. This way we'll explore them in the context of when they are most useful.

Listing the words alphabetically works, but it is more useful to group them. Below are the groupings in addition to a concise description of how they help us code:

Custom Keyword Helpers

  • var - helper for declaring a reusable value by a custom name
  • function - helper for declaring a reusable function by a custom name

Instance Helper

  • new - helper for creating unique function instances

Code Flow Keywords

  • if - helper for guiding the engine to read certain code
  • else - helper for guiding the engine to read certain other code
  • return - helper for a function to provide the result of its work

Literal Value Keywords

  • true - helper for validating code flow
  • false - helper for validating code flow
  • null - helper for the special "absence of a value" value

Debugging Keyword Helper

  • debugger - helper for debugging

The concise description accompanying each keyword may not make complete sense at the moment. This is OK. The takeaway is that these are the ten reserved keywords of JavaScript that you want to focus on. We'll better understand what they do for us as we explore more code.

Non-Reserved Keywords - JavaScript

In the snippet above, there is no example of a non-reserved JavaScript keyword. There are around seventy in JavaScript however. With our subset approach there are six that we care about. They each specialize in working with common types of values.

  1. Date - helper for working with dates
  2. Error - helper for working with errors
  3. JSON - helper for reading and writing data
  4. Math - helper for doing math
  5. Number - helper for working with numbers
  6. String - helper for working with strings

The Date helps us work with dates and time. Errors you understand generally, but we'll explore them in the context of code in the Errors section at the end of this chapter. JSON pronounced "Jason" is likely foreign. JSON is useful for reading and writing in a data format by the same name. The format is really useful in communicating between clients and servers. Math provides a bunch of functions that help coders do complex work with numbers. It also allows us to do simple work with numbers like rounding. Number helps us do more generic work with numbers. Lastly, String helps us work with characters and natural language words that we don't want the engine to interpret as keywords, operators, or statements.

Non-Reserved Keywords - Environment

There are four non-reserved environment keywords in the snippet above. They are document, body, style, and backgroundColor. Each is parented by the former. We'll explore how this keyword parenting is possible in the Expressions section that soon follows.

The document has a parent too. This parent is special and is known as the host object. The host object in a browser environment is the window object. The window object provides the runtime APIs we learned about in the Event Loop section of the Interactive Code chapter. As a result, the use of document and window.document are interchangeable. As an aside, the document provides APIs for us to update our HTML during execution time. This is exactly what we want–and do–in our makeBackgroundBlack function.

Now is a great time to reinforce that professional coders don't remember all the runtime APIs (there are 700+). They reference resources just like beginners. We will do the same and simply focus on knowing about the special window object. Over time we'll memorize certain APIs that we use often.

I do however recommend exploring the list of all the web APIs sometime. The effort enables you to grasp the big picture of what is possible in the browser. You will be impressed and you'll likely find many that peak your interest.

Non-Reserved Keywords - Custom

In the snippet above, there is one non-reserved custom keyword. It is makeBackgroundBlack. There are naturally an infinite amount of custom keywords. Remember that the computer doesn't care what the keyword name is when it is custom. It just cares that it is unique. We could have instead named our makeBackgroundBlack function a and the snippet would instead be:

function a() {
  document.body.style.backgroundColor = '#000000';
}

a();

The functionality is the same even though there are fewer characters. As a result there is less code–a smaller payload–to send from a client to a server and vice versa. Remember however that we are coding for humans first. Naming keywords meaningfully is the primary goal. We can decrease the payload size later through the aforementioned minification process among others. The takeaway is that the engine only cares that custom keywords are unique. The name itself is useful for us coders.

You will notice that the document, body, style, and backgroundColor environment identifiers are left untouched. This is because they are not custom keywords, the runtime expects them to be associated with certain values. As such, they would not be shortened through minification.

It is worth noting that when naming custom keywords there are a set of rules. I will list them here for general familiarity, but there is a subset to instead focus on. Custom keywords can use the following characters:

  • a-z (lowercase characters)
  • A-Z (uppercase characters)
  • _ (underscore character)
  • $ (dollar sign character)
  • 0-9 (number characters when not the first keyword's character)

There are three common case styles that are used as a result of the above rules:

  1. upper camel (ex. UpperCamelCase)
  2. lower camel (ex. lowerCamelCase)
  3. underscore (ex. underscore_case)

I recommend using the lowerCamelCase just like we did with makeBackgroundBlack. We'll additionally use the UpperCamelCase style for certain functions, but we'll cover why in the Functions section later. The takeaway is that you can use all the rules above, but it is much simpler to stick to the lowerCamelCase and UpperCamelCase styles.

It is worth noting that these case styles exist for a reason. If blank spaces were allowed then our makeBackgroundBlack keyword would be make Background Black. The engine would instead see three keywords as opposed to one. The lowerCamelCase and UpperCamelCase styles exist to mitigate this issue while maintaining readability.

Now that we've explored the various keyword types, now is the best time to explore the types of expressions–or values–a keyword can represent.

#

Expressions

Expressions are values. More precisely an expression is a piece of code that results in a value. The term expression is used to denote the fact that the engine may need to do some work to get the value—the expressed result. If this was not the case then the official term would be value instead of expression.

In both examples below the value is the number 360. The latter of the two requires work where the former does not:

  • 360 (literal value expression)
  • 300 + 60 (arithmetic expression)

Both examples are not really useful on their own however. An expression—the resulting value—becomes useful when used in the context of a code statement. A statement always consists of at least one expression. We will explore statements in greater detail in the Statements section but here are a few examples so you may begin to intuit how values are useful:

  • var maximumRotation = 360;
  • if(currentRotation > 360) { currentRotation = 360; }
  • function getMaximumRotation() { return 360; }

The relationship of words to a sentence in natural languages is similar to the relationship of expressions to a statement in programming languages. A word is a basic unit of meaning just like an expression is. These units in aggregate provide greater meaning as a sentence in natural language or a statement in a programming language. In the Statements section and in time you'll begin to grasp what constitutes a valid statement.

Types & Forms

We've talked a lot about values, but we have not explicitly explored the built-in types of values in JavaScript. We only care about six of the seven types due to our subset approach. The types are organized in two groups:

  1. Primitive Values
    • null (null)
    • undefined (undefined)
    • Boolean (true & false)
    • Number (360)
    • String ("one or more characters wrapped in double quotes" or 'single quotes')
  2. Complex Values
    • Object ({} & [])

The examples within parenthesis above are all examples of the literal form of the respective value type. This form is most common and preferred. It is important to know that JavaScript has another way to create values however. This other way is called the constructor form. The constructor form leverages the new operator keyword in addition to an environment keyword. This environment keyword denotes the value's specific Object type. Here is the same list using new and the respective type's environment keyword (which is a named shortcut to a function):

  1. Primitive Values
    • null (only literal form)
    • undefined (only literal form)
    • Boolean (new Boolean(true) & new Boolean(false))
    • Number (new Number(360))
    • String (new String("one or more characters wrapped in double quotes") or new String('single quotes'))
  2. Complex Values
    • Object (new Object() & new Array())

The literal form is best for built-in types where the constructor form is best for non-built-in types. Programmers like shortcuts, so this is why the literal form is preferred. The constructor form is useful—required really—for specific types of environment Objects like the aforementioned Date and Error among others. Custom types also require the constructor form.

Primitive Values

Since primitive values are so fundamental to JavaScript, let's explore each of them in a little more detail. Below is a small program that will be further referenced as an example use of each primitive. Comments are intentionally absent so you can practice the thinking in three zoom levels technique. Pretend that the code is running in a browser whose HTML structure:

  1. can display multiple artboards on a surface
  2. has a "Create Artboard" button (<button id='create'>Create Artboard</button>)
  3. has a "Delete Artboard" button (<button id='delete'>Delete Artboard</button>)
  4. has downloaded, compiled, and is executing our program (<script src='assets/js/artboards.js'></script>)
var createArtboardButton = document.getElementById('create');
var deleteArtboardButton = document.getElementById('delete');
var artboards = [];
var artboardInFocus;

function setupEventListeners() {
    createArtboardButton.addEventListener('click', onCreateArtboardButtonClick);
    deleteArtboardButton.addEventListener('click', onDeleteArtboardButtonClick);
}

function updateArtboardInFocus(artboard) {
    artboardInFocus = artboard;
}

function deleteArtboardInFocus() {
    var artboardInFocusIndex = artboards.indexOf(artboardInFocus);
    artboards.splice(artboardInFocusIndex, 1);
    artboardInFocus.removeSelfFromSurface();
    artboardInFocus = null;
}

function onCreateArtboardButtonClick() {
    var artboard = new Artboard();
    artboards.push(artboard);
    artboard.addSelfToSurface();
    updateArtboardInFocus(artboard);
}

function onDeleteArtboardButtonClick() {
    if (artboardInFocus === undefined) {
        alert('No artboard to delete. Try creating one first.');
    } else if (artboardInFocus === null) {
        alert('No artboard to delete. None of the ' + String(artboards.length) + ' artboards are in focus.');
    } else {
        deleteArtboardInFocus();
    }
}

setupEventListeners();

Null

The special null value denotes the intentional absence of a value. This special value is not automatically assigned in JavaScript. It must intentionally be assigned to a keyword by a coder. Though null represents the absence of a value, it is technically a value itself. A little weird I know. This is what makes it "special".

In the artboards.js code above we use null in the deleteArtboardInFocus function:

artboardInFocus = null;

and in the onDeleteArtboardButtonClick function:

else if (artboardInFocus === null) {
    alert('No artboard to delete. None of the ' + String(artboards.length) + ' artboards are in focus.');
}

By using null we can provide a more intentional path for the engine to execute code when the program is being interacted with.

Undefined

The special undefined value denotes the unintentional absence of a value. This special value is automatically assigned in JavaScript. It is the default value for variable declarations. Additionally, it is the value returned when a nonexistent keyword is accessed. These two aspects make it "special".

In the artboards.js code above undefined is automatically used in the variable declarations section:

var artboardInFocus;

and additionally in the onDeleteArtboardButtonClick function:

if (artboardInFocus === undefined) {
    alert('No artboard to delete. Try creating one first.');
}

If the first interaction with the program is to click the "Delete Canvas" button then the above alert code would run. If we did not check for undefined and null prior to executing deleteArtboardInFocus() we'd get an Error. This would happen because we can't delete an artboard that does not exist. The above examples illustrate why the undefined and null values are useful.

Boolean

The Boolean type denotes one of two values: true or false. Remember the bit? This is JavaScript's formal approach to the same goal of defining one of two states. The bit's 0 is the Boolean's false. Its 1 is the Boolean's true.

In the artboards.js code above the onDeleteArtboardButtonClick function implies the use of a Boolean value in two places. Can you spot them before reading on?

Here is a more explicit version of the same functionality using the constructor form:

var isArtboardInFocusUndefined = Boolean(artboardInFocus === undefined);
var isArtboardInFocusNull = Boolean(artboardInFocus === null);
if (isArtboardInFocusUndefined) {
    alert('No artboard to delete. Try creating one first.');
} else if (isArtboardInFocusNull) {
    alert('No artboard to delete. None of the ' + String(artboards.length) + ' artboards are in focus.');
} else {
    deleteArtboardInFocus();
}

Both the implicit (literal form) and the explicit (constructor form) versions result in the same code flow. Since programmers like shortcuts you will almost always see the implicit version when working with if, else if, and else. Admittedly the implicit version falls in the right and better categories. Feel free to use whichever version makes more sense to you.

The takeaway is that Boolean values—in either literal or constructor form—are fundamental to controlling code flow.

Number

The Number type denotes numbers. Impressive I know. These numbers can be whole (-360, 0, and 360) or fractions (-.36, .36, and 3.6). They can be negative or positive too. There are technically limits to a number value in JavaScript, but for our subset approach we can ignore them. If you ever need to work with extreme whole numbers (positives or negatives in the quadrillions) or similarly extreme fractions then feel free to dig deeper. Thought so.

In the artboards.js code above we use a Number twice in the deleteArtboardInFocus function:

var artboardInFocusIndex = artboards.indexOf(artboardInFocus);
artboards.splice(artboardInFocusIndex, 1);

and once in the onDeleteArtboardButtonClick function:

alert('No artboard to delete. None of the ' + String(artboards.length) + ' artboards are in focus.');

The first snippet uses an evaluated number assigned to artboardInFocusIndex in addition to the literal 1 value. The two lines of code work together to:

  1. find where in the artboards array the artboardInFocus is
  2. use the Array's built-in splice function to remove that artboard (the artboardInFocus)

The second snippet uses the evaluated artboards.length value to get the number of total artboards that exist. This allows us to display an up-to-date message using the correct artboards count.

String

The String type denotes "one or more characters wrapped in double quotes" or 'single quotes'. There are seven examples of String values being used in the artboard.js code above.

Strings are useful for defining names, event types, and messages among other things. Concrete examples of this are the use of create and delete, click, and the alert strings respectively. It is worth noting that double quoted and single quoted strings are valuable in different scenarios:

  • "The artboard's size is too small."
  • '"The artboard is too small," she said.'

There is one specific example from the onDeleteArtboardButtonClick function I'd like to call out:

alert('No artboard to delete. None of the ' + String(artboards.length) + ' artboards are in focus.');

Since artboards.length is a number, we explicitly convert it to a String using the String function. We additionally use the + (concatenation operator) twice to make one large string from three smaller strings. This latter fact will soon be explored more in the Operators section.

The takeaway is that the String type prevents the engine from processing its characters as keywords or other value types. For example, 'null', 'undefined', 'true', 'false', and '360' are all String values because they are wrapped in quotes. If we removed the quotes they would instead be examples of the null, undefined, Boolean, Boolean, and Number types respectively.

Complex Values

...

Copy vs. Reference

Custom keywords—declared with the help of var and function—are how we declare our own named shortcuts to values. These values are one of the six types above where function is a specific type of Object. When a primitive value is assigned to a keyword, the keyword holds a copy of the value. A complex value assigned or associated to a keyword is instead a reference to the value. So primitive values are always copies and complex values are always references. This distinction is important because references, unlike copies, enable the sharing of:

  • functionality
  • structured data

We already know that sharing functionality—sharing functions—provides us a simple and reusable way to do work. This shared functionality is what gives us APIs. Code would not be able to talk to other code otherwise. Data and specifically structured data have not been explicitly covered yet however.

Data is simply any primitive or complex value. Structured data is always a complex value. It parents other values in an:

  • Object
  • Array

Each allows the organization of data—primitive or complex values—using a particular structure. These structures are:

  • Object as Tree
  • Array as List

The literal form of each is {} and [] respectively. An Object literal uses braces and an Array literal uses brackets. Braces are curved and brackets are straight. These visual differences are clues reminding you which structure belongs to which literal form:

  • Object/curves/braces/{}
  • Array/straight/brackets/[]

For reference, here is a visualization of the anatomy of each structure:

Object and Array Anatomy

#Object and Array Anatomy

Let's explore some examples of code to really drive home Objects and Arrays. First, here is an example of a few custom Objects in literal form. They are each assigned to a keyword—using var—for easier understanding and later reuse:

var pen = {
    name: 'Pen',
    color: '#000000',
    thickness: 1 
};
var highlighter = {
    name: 'Highlighter',
    color: '#FFFF00',
    thickness: 3
};
var paintbrush = {
    name: 'Paintbrush',
    color: '#0000FF',
    thickness: 8 
};

Now here is an example of a custom Array in literal form. The list is assigned to a keyword too—again using var—for easier understanding and later reuse:

var drawingTools = [pen, highlighter, paintbrush];

The code for each drawing tool type makes the most sense when structured as a tree—an Object. When organizing all the drawing tools, it makes the most sense to structure them as a list—an Array. Over time you will learn to intuit when to use which type.

On their own, all four variable declarations above are not all that useful. An Object is most useful when code can access its nested keywords and thus its nested values. Likewise, an Array is most useful when code can iterate its values. We'll cover what it means to iterate an array's values in the Functions section. Accessing an object's nested keywords however transitions us right into learning about Operators.