Understanding JavaScript Data Types (Through the Lens of D&D & Other TTRPGs)
Tags: javascript, beginners
Published: 2022-04-17 22:35:57

Humans _love_ to make sense of our world and ourselves through categorization; whether it's understanding biological life through taxonomy, ourselves through identity labels, or even our fictional RPG characters through their "classes," we want to break things down into their smallest components and group them based on certain characteristics and similarities. Computer programming, and JavaScript in particular, is no different. 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ouopcyy2vib6zr5tr3e3.png)

**What Are Data Types & Why We Need Them**
Just as the Dungeon & Dragons RPG system has a set of rules that governs how the different character classes behave, programming languages have a type system that classifies how the built-in data structures should behave depending on their "type." 

As Aaron Krauss describes it in his [blog](https://thecodeboss.dev/2015/11/programming-concepts-static-vs-dynamic-type-checking/) "Programming Concepts: Static Vs. Dynamic Type Checking":

> _A type system is a collection of rules that assign a property called type to various constructs in a computer program, such as variables, expressions, functions or modules, with the end goal of reducing the number of bugs by verifying that data is represented properly throughout a program._

In other words, data types refers to the 'type' of value we're working with. It's a way to categorize the information/data that we use so that we interact with our code appropriately. 

Every value is associated with a certain data type that falls into one of these *main categories: 

- Number
- String 
- Boolean 
- Array 
- Object 
- Function 
- undefined 
- null
- NaN 

```
let partyMembers = 9; // Number
let partyName = 'The Fellowship'; // String
let allSurvive = false; // Boolean

// Array
let partyMemberAges = [87, 38, 2000, 50, 2000, 28, 36, 139, 40]; 

// Object
let partyClasses = {
Aragorn: 'Fighter',
Sam: 'Paladin',
Gandalf: 'Wizard',
Frodo: 'NPC',
Legolas: 'Fighter',
Pippin: 'Rogue',
Merry: 'Bard',
Gimli: 'Fighter',
Boromir: 'Fighter'
}

//Function
function averagePartyAge(array){
let totalAge = 0;
for(var i = 0; i < array.length; i++){
totalAge += array[i];
}
return totalAge / array.length;
}

let bestMember; // undefined
let worstMember = null; // null
let middleEarth = partyName * allSurvive; // NaN
```

These data types have various properties and methods associated with them that allow the JavaScript interpreter to correctly handle this data. For example, the interpreter treats numbers differently from strings, so while `2 + 2` and `'2' + '2'` look very similar, they are actually different data types: Numbers and Strings, respectively. Without understanding their type, the interpreter would not be able to execute this code correctly. So, now that you know _why_ we need these data types, let's talk about each of the data types in turn.

**Number**
The Number data type represents all numeric values (both positive and negative). Categorizing these numeric values as Numbers allows us to use the static properties and methods attached to that data type. We are also able to perform calculations on Number data types by using arithmetic operations. 

```
let currentXP = 6; // resolves to the number 6
let previousXP = 1000; // resolves to 1000
let totalXP = currentXP + previousXP; // resolves to 1006
```

`currentXP` and `previousXP` are Number data types because they have a numeric value and are not written as strings, which means we are able to add these values together. 

**String**
The String data type is a collection of characters between either single or double quotes. Strings allow us to represent data in text form. 

```
let moreExperience = 'Your character gets 500 more xp!'; 
let xpUpdate = '500'; // resolves to a string of '500' 
```
The variables `moreExperience` and `xpUpdate` are strings, despite also having numbers in them. If you were to try and add `xpUpdate + currentXP`, the JavaScript interpreter would type coerce `currentXP` as string rather than adding the values together to create a sum. So, instead of outputting 506 as you might expect, it would concatenate these values as a string and return 5006.

Arithmetic operations cannot be used on this variable unless it is first converted to the number data type. 

**Boolean**
Booleans hold only one of two values: true or false. Booleans are most useful when storing values with clear binaries, such as yes/no or on/off. When only one of two values are possible, Booleans are usually used.

```
let haveSpellSlots = true;
let spellsCount = 0;
if(spellsCount <= 0){ 
haveSpellSlots = false; // change to a Boolean of false
}
console.log(haveSpellSlots); // prints => false
```

The variable `haveSpellSlots` is assigned to the Boolean `true`, denoting that yes, we have spell slots available to use. If the value of `spellsCount` is less than or equal to 0, however, the `haveSpellSlots` is reassigned to a Boolean of `false`.

**Array**
The array data type categorizes objects that can store a collection of items in an indexed list. These data types can be expanded, and thus are also considered complex data types. Arrays are denoted by the use of `[brackets]` when assigned a variable. 

```
let characterTraits = ['1', 5, [], true, 'Adora', 'Druid']; 
console.log(typeof(characterTraits)); // prints => 'object'
console.log(Array.isArray(characterTraits)); // prints => true
```
The variable `characterTraits` is assigned to an array containing 6 elements. 

**Object**
The object data type is used for storing a collection of keyed data and other data types (like functions). Essentially objects are variables that can store a multitude of values. Like arrays, objects are also considered a complex data type. 

```
const dndCharacter = {
    class: 'Druid',
    level: 1,
    cantrips: ['Guidance', 'Mending', 'Shillelagh'],
    lvlSpells: ['Cure Wounds', 'Healing Word'],
    haveSpells: false,
}
```
The above `dndCharacter` object has keys of `class`, `level`, `cantrips`, `lvlSpells`, and `haveSpells`, each of these containing different types of values. 

**Function**
The function data types denote a block of statements that perform some task or calculation. Functions are a complex data type. 

```
function whoIsDM(name){
console.log(`${name} is the Dungeon Master`);
}

whoIsDM('Chris'); // calls the function

'Chris is the Dungeon Master' // logged to the console
```

**undefined**
The undefined data type represents undefined values or values that have not been initialized. 

```
let nextCampaign; 
console.log(nextCampaign); // prints => undefined
```

The `nextCampaign` variable is declared, but has not been assigned to any value, thus when it is later called, it returns undefined. 

**null**
The null data type represents none or the absence of value. These occur when the programmer intentionally initializes a piece of data to null.

```
let empty = null; // assigned a variable to null
console.log(empty); // prints => null
```

**NaN**
NaN is a special number type that stands for Not-A-Number. NaN happens when an arithmetic operation is applied to a invalid or undefined value. 

```
console.log('Do not split the party!' / 9); // prints => NaN 
```

**Primitive vs. Complex Data Types**
In addition to the above categories, cata can can also be classified as either primitive/simple or complex data types. We can use the typeof operator to help us determine what type a piece of data is. 

Primitive/Simple data types include: Numbers, Strings, Booleans, NaNs, undefined, and null. These data types are considered simple because they are atomic and immutable, which means they do not aggregate other values (like arrays and objects). 

Also, simple data types are copy by value meaning that any code that acts on these data types would simply be working with a copy of the original, rather than modifying the original value itself.

So, manipulating a primitive data type would result in an entirely new simple value. Variables can hold the actual values of primitive types, but this necessitates that simple data types be a fixed size. 

Complex data types include: Objects, Arrays, and Functions. Unlike simple data types, complex data types can collect and add to the values contained within. These data types are store as a reference, meaning that we can act directly upon the original, thus changing it. Because complex data types can be added to and manipulated, they have an indefinite size. 

```
let characterName = 'Adora'; 
let druidName = 'Adora';  
```
The variable `characterName` is a simple data type with a string. The value 'Adora' cannot be mutated or altered directly. `druidName` is an entirely new variable with a new value with no relationship to the above variable. Because strings are immutable, you cannot use methods to alter the original. You can see this demonstrated below. 

```
druidName.toUpperCase(); // nothing happens
console.log(druidName); // prints => 'Adora'
druidName = 'Adora Mossweather'; // reassigned
console.log(druidName); // prints => 'Adora Mossweather'
```

Nothing happens to `druidName` as it cannot be directly altered with these methods. However, variables _can_ be reassigned, as shown by the variable `druidName` which has taken on a new value.

Unlike Strings, Numbers, and Booleans, Objects _can_ be mutated by using an object method on the original. For example:

```
const dndCharacter = {
    class: 'Druid',
    level: 1,
    cantrips: ['Guidance', 'Mending', 'Shillelagh'],
    lvlSpells: ['Cure Wounds', 'Healing Word'],
    haveSpells: false,
}

dndCharacter.name = druidName; // adds druidName property 
console.log(dndCharacter); // prints => updated object
```
Because `dndCharacter` is an object, we are able to include a new property called `name` with the `druidName` as its value.

**Copy by Value vs Copy by Reference**
All primitive data values are copied by value, while all complex data types are copied by reference. What that means is a variable can reference another variable, but the value stored is merely a replica of the original, as demonstrated below. 

```
characterName = druidName; // points to a copy of druidName
console.log(characterName); // prints => 'Adora Mossweather'

characterName = 'Adoria Mossy'; // reassigns to a new value

console.log(druidName); // prints => 'Adora Mossweather'
this variable was not affected by the above variable's reassignment

console.log(characterName); // prints => 'Adoria Mossy'
```
The `druidName` variable was not affected by the `characterName` variable's reassignment.

Complex data types, however, are copied by reference which means that the original object, array, or function can be modified directly. 

```
dndCharacter.lvlSpells.push('Barkskin', 'Flaming Sphere', 'Moonbeam'); 
// adds 3 more elements to the spells array located in the dndCharacter object


console.log(dndCharacter); // prints => object with the values

{
  class: 'Druid',
  level: 1,
  cantrips: [ 'Guidance', 'Mending', 'Shillelagh' ],
  lvlSpells: [
    'Cure Wounds',
    'Healing Word',
    'Barkskin',
    'Flaming Sphere',
    'Moonbeam'
  ],
  haveSpells: false,
  name: 'Adora Mossweather'
}

let dndParty = dndCharacter;
console.log(dndParty); // prints => same object as dndCharacter

dndParty.companions = ['Percy', 'Roark', 'Vox']; 
console.log(dndCharacter.companions); // prints => array

dndParty = {};
console.log(dndParty); // prints => {}
```
The variable `dndParty` points directly to the same object as the variable `dndCharacter`. Because of copy by reference, when we add a new key property to `dndParty`, `dndCharacter` is updated as well. It is only when we reassign `dndParty` to a new empty object that it is no longer referencing the object stored at `dndCharacter`.

And, that, in a nutshell is what JavaScript data types are and how you can identify them in your own code. 

*_BigInt and Symbols are also a data type, but we will not be touching on those in this article._