JavaScript: Data Types
Page 6
Data Types
Introduction
Learn about simple and complex JavaScript data types.
Tap sets of colored buttons to see the JavaScript typeof
operator's
return value for various JavaScript types. Tap buttons to
see return values from various data type features.
See string, number, boolean, null, undefined, class, key-value associative arrays, array, function, Symbol, BigInt, Set and Map types.
Most of the JavaScript source code's in files datatypes.js, symbols.js and p6.js.
typeof Operator
The JavaScript typeof
operator
returns the data type of its operand.
The typeof
operator recognizes
string, number, boolean
and undefined
primitive data types.
The typeof
operator recognizes
either object
or function
complex data types.
Primitive Types
JavaScript primitive data types hold
one single value of data.
Primitive types cannot contain, or hold,
more than one value.
The typeof
operator returns
string, number, boolean
or undefined
,
for primitive data types.
string
String data type contains a set of characters
as one string. For example variable s
below, holds the string, Web Developer
.
Assign strings with single or double quotes,
as follows.
// Double quotes: let s = "Web Developer"; let sName = "Tanya"; // Single quotes: let sLastName ='Doristica';
number
Number data type contains a digit or decimal value.
Variable n
below, holds the decimal number, 3.14
.
var n = 3.14;
Assign numbers without quotes.
var iNumerator = 2; var iDenominator = 4;
boolean
The boolean data type contains either
true
or false
values.
Boolean variable b
below, holds the value true
.
var b = true;
No Value Types
Data types with no value
include null
and undefined
.
No value types include both undefined
,
which is considered a primitive type
and null
which is considered
a complex type.
The typeof operator returns object
for null
values and undefined
for unassigned variables or properties.
undefined
If a variable holds no value
then it's undefined.
A variable that has never received
a value is undefined
.
For example variable u
below,
is undefined.
var u;
null
It's better practice to
assign a variable the value null
,
than to leave it undefined.
var f = null; typeof f;
var q; typeof q;
Case Sensitive
The complex type, null
has no value, yet
when spelled with all capital letters such as, NULL
,
an error's triggered.
For example you might see,
Uncaught ReferenceError: NULL is not defined
.
var e = null;
var z = NULL;
Tap Boxes: Primitive typeof Operator
Tap four boxes below, to see
the result of the typeof
operator on data types string, number, boolean
and undefined
.
String & Number
var s = "Web Developer"; typeof s;
var n = 3.14; typeof n;
Boolean & Undefined
var b = true; typeof b;
var u; typeof u;
Complex Data Types
The typeof
operator returns
either object
or function
for complex data types.
Object
Many different structures identify as
object data types.
For example the typeof
operator
returns object
for
{animal:'Cheetah', speed:60},
['a','b','c'], Date
and null
.
Function
The typeof operator returns function
for both class and function declarations.
Tap Boxes: Complex typeof Operator
function fHelloWorld(){alert('Hello World')} typeof fHelloWorld;
var j = new Date(); typeof j;
Avoid new
Object Types
JavaScript allows simple
data type declarations with the keyword new
and the type constructor, as follows.
However such declarations aren't needed
and they decrease code performance.
var n = new Number(); var s = new String(); var b = new Boolean();
BigInt
JavaScript numbers by default, use 64 bit floating point values.
The default number range is positive or negative (253 - 1)
.
Therefore developers can operate with no more
than fifteen digits per integer.
The new BigInt data type allows developers to operate on integer values that have more than fifteen digits.
Declare BigInt
Create BigInt with either the
BigInt()
cast or
prepend a string of digits with n
,
as follows.
let nBigInt1 =4681012141618202224262830n; let nBigInt2 = BigInt(4681012141618202224262830);
Tap a Box for Typeof with BigInt
n
.
let nBigInt1 =4681012141618202224262830n; typeof nBigInt1;
BigInt()
let nBigInt2 = BigInt(4681012141618202224262830); typeof nBigInt2;
BigInt Operators
All JavaScript number operators apply to BigInt datatypes. However you can't mix number and BigInt types within expressions. Tap a box for BigInt multiplication or division.
let n1 = 7234123456789103n; let n2 = 8116199254740995n; let nProduct = n1 * n2;
// BigInt operates with BigInt // not with Number nBigInt1/2n;
BigInt Exceptions
Try-catch blocks may capture exceptions when developers attempt to perform mathematical operations on BigInt with Number. Tap a box for BigInt with Number multiplication or addition.
let nBigInt2 = BigInt(4681012141618202224262830); let n2 = 3; let x = nBigInt2 + n2;
let x = 8116199254740995n * 2;
Complex Types
Complex types include any variable or constant
which contains more than one value such as class declarations, class references,
array, key-value associative arrays and Date.
The typeof
operator returns function
for both functions and class declarations.
The typeof
operator returns object
for class references and most other complex data
types.
Tap Boxes: Complex typeof Operator
Class Types
class Cat { constructor(name, type) { this.name = name; this.year = year; } } typeof Cat;
class g { constructor(){} f(){}} var g1 = new g(); typeof g1;
Object Types
const dog = {type:"Poodle", color:"white"}; typeof dog;
var a = ['a','b','c']; typeof a;
Tap for Odd Results
Some calls to typeof should probably return undefined
from the typeof operator,
such 3/0
or ‘abc’/3
.
Mathematically 3/0
is undefined.
If I pass 3/0
or
to the typeof operator, in my Web browser, the value returned is
always abc
/3number
.
Perhaps any mathematical operation returns
a number type. Notice when you tap buttons, that operation return values
are Infinity
and NaN
. The typeof
operator then determins that both Infinity
and NaN
are
of type number
.
var n = 3/0; typeof n;
var p = "abc"/3; typeof p;
Positive, Negative Infinity
Just display the result of
3/0
and -3/0
.
Don't call the typeof operator.
With the Firefox browser on my OS, the
following two statements evaluate to
Infinity
and -Infinity
.
They probably should evaluate to NaN
or undefined
.
Tap for Result: NaN, Infinity
Just display the result of true/0
and
. Don't call typeof.
My browser displays abc
/3NaN
for
and
abc
/3Infinity
for true/0
.
var n = true/0; // display n n;
var p = "abc"/3; // display p p;
See Data Types!
Tap boxes to see a large set of data types, below. JavaScript file, datatypes.js, declares each variable.
Tap Boxes
Each box displays the value returned from the typeof operator.
var a = ['a','b','c'];
var b = 9.1;
String & Boolean
var c = "Developer";
var d = false;
Function, Associative Array
Tap to see return values for a function and associative array.
function h(){};
var i = {key1:'value1',key2:'value2'};
Symbol, Exponent E
Symbols maintain hidden values, therefore an attempt to display the symbol itself triggers an exception.
Please read more about the new Symbol type, later.
Exponent E
Exponent e
multiplies a number by the power of ten.
If the operand for e
is positive, then the decimal
point moves to the right, multiplying the number by powers of ten.
If the operand for e
is negative, then the
decimal point moves to the left, dividing the number
by powers of ten.
Exponent e-2
appended to the number 3
in 3e-2
, moves the
decimal point, on the number three, two digits to the left.
Therefore the product equals 0.03
.
var l = Symbol('letter');
var m = 3e-2;
Let, Var, Const
Variables declared with keywords let
versus var
modify scope.
A var
has scope in its current block and
all sub blocks.
A let
has local scope. Therefore
you can declare the same variable name
with let
in separate blocks, but not sub blocks, and
the variables will not affect each other.
You can redeclare a var
, and it will contain its
original value. You cannot redeclare a let
.
OK
After the following two lines, below, execute,
test
still evaluates to Test
,
even if the second test
variable
was declared inside a sub block such as, var test = "Test"; {var test;}
var test="Test"; var test;
Invalid Let
The following two lines generate an error. Don't declare two let variables with the same name in the same block or in sub blocks.
let test="Test"; let test;
Invalid Let: Block & Sub Block
Invalid application of the let
keyword renders JavaScript incapable of execution.
For example the following function disables the entire
JavaScript file with the error
Uncaught SyntaxError: redeclaration of let n
.
Any try-catch
block never has a chance to execute.
Notice declaration of let n
in a super block
and declaration of let n
in a sub block.
function letTest(sId){ var e = getElement(sId); // Super block: let n = 16; // Sub block: for(let n = 0; n < 3; n++){ e.innerHTML += n+"<br>"; } e.innerHTML += n + ": outside of block."; }
What's the Problem?
You can declare variables with the same name
as both let
and var
only when the let
variable has a narrower scope.
You can declare:
var n = 1; { // Narrow scoped // to this block: let n = 2; }
You cannot declare:
// Wider scoped // outside of block: let n = 1; { var n = 2; }
Let Versus Var in Action
Tap a box.
The values for both var n
and let n
display.
The let value has a more narrow scope
and maintains its value within
its containing block.
The var value has a wider scope
and maintain its value outside
the narrower block.
function letTest(sId){ var e = getElement(sId); var n = 16; for(let n = 0; n < 3; n++){ e.innerHTML += n+": inside block.<br>"; } e.innerHTML += n + ": outside block."; }
function letTest2(sId){ var e = getElement(sId); var n = 32; // Block declared // with simple curly braces: { let n = Math.sqrt(4); e.innerHTML += n+": inside block.<br>"; } e.innerHTML += n + ": outside of block."; }
Tap Boxes
var test="Test"; var test;
let test="Test"; let test;
Const
Variables declared with const
remain the same value and
block scope.
You cannot redeclare constant variables
within a block, with the same name, but different value.
Array properties, where the array's a const
, can
be reassigned. The array itself cannot be reassigned but the array
property can be reassigned. That may seem like a subtle
difference but it's not.
Don't expect const
to protect
array entries. Any developer can change const
array properties.
Tap to Trap Errors
The following boxes trap errors, if something invalid attempts to process.
Use try-catch
or
try-catch-finally
blocks to process errors within your code.
Return or Finally
If code doesn't return after catching an exception, then it will resume. If code resumes then be sure to avoid accessing variables which may have triggered an error.
You may also include a finally
block
to execute any safe
code, regardless whether
an exception happened or not.
Safe code includes just the properties which
may not have triggered an exception.
Click to Test - Throws an Error
function createError(sId){ let e = getElement(sId); try { // Undefined function: fNotDefined(); } catch(err) { e.innerHTML = err.message; return; } }
Click to Test - c3,c4 In Scope
function testConstScope(sId){ let e = getElement(sId); // Assign constant // the value 1, once. const c1 = 1; try{ // You can't assign // c1 a different value. c1 = 2; } catch(ex){ // Display the error: e.innerHTML = ex.toString(); return; } // If there was no // error, then display // the value of c1: e.innerHTML = c1; }
Change Const Array Entries
You can change the value of an entry
in the constant array named, aDigits
.
You cannot change the entire array.
Change Entry At Zero:
const aDigits = [1,2,3]; aDigits[0] = 5;
Change Entire Array
const aDigits = [1,2,3]; try{ aDigits = [4,5,6]; } catch(ex){ e.innerHTML = ex.toString(); }
Key-Value Pairs
Key-value pairs can be accessed in a number of
ways.
For example value1
in the following code, could be accessed
with v = i[key1];
or v = i.key1;
.
However v = key[0]
does not work. Let's try it.
Tap Boxes: Associative Array Entries
// Declare associative array: var i = {key1:'value1',key2:'value2'}; // Display this value: i['key1'];
// Declare associative array: var i = {key1:'value1',key2:'value2'}; // Display this value: i.key2;
Associative Arrays
You can't access the value of an associative
array by index number.
You can access the keys of an associative array
with a for-in
loop as follows.
const dog = {type:"Poodle", color:"white"}; function assocArrayByIndex(sId){ let e = getElement(sId); if (dog[0]){ e.innerHTML = dog[0]; } else{ e.innerHTML = "Inaccessible dog[0]."; } }
const dog = {type:"Poodle", color:"white"}; function showKeys(sId){ let e = getElement(sId); e.innerHTML = "Keys from dog associative array:<br>"; for (var key in dog) { e.innerHTML += key+","; } }
Plus Sign
You can concatenate strings or add numbers with the plus sign. Adding a number to a string or a string to a number can certainly cause confusion.
Strings
On my OS with the FireFox Web browser, if any element in an expression is a string, then the entire expression evaluates to a string. Every number's converted to string representation and appended to a string. It doesn't matter if the last element or the first element's a string.
However if all elements are numbers then the type's a number and results process with addition.
Tap to Evaluate: String or Number
Notice if the first entries are numbers, they're numerically added until the processor finds a string. The string's appended and the result's a string.
"1" + 2 + 3
1 + 2 + "3.14"
Tap to Evaluate: String or Number
Any string operand causes the processor to return a string. Numbers are concatenated as strings once a string's encountered.
("1" + 2) + 3
(1 + 2) + 3
Symbol
JavaScript Symbols were added to ES6 as primitive data types.
Symbols represent unique identifiers.
Create a new symbol without the new
keyword.
Optionally include an identifier as the symbol's argument.
Symbols are stored in the registry.
Symbols are immutable and therefore cannot be changed.
Symbols allow developers to create variables with values that no one can read or modify. Code can't reassign values to a symbol.
You can create symbols with identifiers for ease of use. However two temporary symbols can have the same identifier, yet different values. Two global symbols cannot have the same identifier. All global symbols have different values and different identifiers.
Always Different Values
Therefore both temporary and global symbols always have unique values. No two symbols maintain the same value. Developers cannot change or read symbol values.
This smakes symbols useful. Once you've created a symbol, regardless of the coded identifier, the value remains fixed.
- Symbol name: variable name of a symbol
- Symbol identifier: developer specified string. More than one local symbol may have the same identifier.
- Symbol value: OS specified value. Only one symbol may have the same value.
- Global symbols maintain state across files. Stored in the symbol registry.
- Temporary symbols dissappear when a file completes execution.
Global Symbols
Global symbols are stored in the global symbol registry. Global symbols remain active as long as an application runs. Global symbols allow only one symbol per identifier.
Global symbols are accessible between JavaScript file execution, within one application. Once the symbol's created it remains active across files.
Methods Symbol.for('id')
and Symbol.keyFor(value)
are inverses.
The first method returns a symbol value by identifier,
if it exists in the global registry.
The second method returns a symbol identifier by value,
if it exists in the global registry.
You cannot have two symbols
with the same identifier in the global registry.
Otherwise Symbol.for('id')
would
not know which symbol to return.
Therefore Symbol.for('id')
either
creates or returns one symbol with one ID
in the global registry.
Global Symbol.for('id')
Symbol.for('id')
retrieves a symbol by ID from the
global registry.
If the id does not exist then
Symbol.for('id')
creates a symbol in the global
symbol registry.
Global Symbol.keyFor(value)
Symbol.keyFor(value)
retrieves
a symbol in the global
symbol registry based on the
unique symbol value.
If the symbol does not exist then
symbol.keyFor(value)
returns undefined or throws an error.
Temporary Symbols
Temporary symbols store in a temporary symbol registry however symbol values are not maintained after a file completes execution. Temporary symbols allow more than one symbol per identifier. Yet each symbol's value's different.
Temporary: Symbol('id')
Symbol('id')
creates a symbol in the temporary
symbol registry.
You can have two, or more, temporary symbols with
the same ID. However each symbol's value
will be different.
Create Temporary Symbols
Declare a local symbol with an identifier.
The following example's identifier is id4
.
The value of the symbol's saved to variable
j
, yet you cannot read that value.
var j = Symbol('id4');
Create Second Symbol: Same Identifier
The following line creates a second local symbol with
the identifier id4
. The previous example
created a symbol with identifier id4
, as well.
Both lines of code may execute within the
same file. Both local symbols will be created
with the same identifier. However
symbol j
and symbol k
maintain different values.
var k = Symbol('id4');
Test Symbols for Equality
Despite the fact that both
j
and k
have the same identifier,
they are different symbols.
if(j != k){alert('equal');
Global Versus Temporary Symbols
You can create two temporary symbols with different keys, or identifiers. You cannot create two global symbols with different keys, or identifiers. Temporary symbols with the same keys, are different symbols. Global symbols with the same keys, are the same symbol.
let e = getElement(sId); s1Local = Symbol('keysame'); s2Local = Symbol('keysame'); e.innerHTML = "s1: "+s1.description; e.innerHTML += "<br>s2: "+s2.description; e.innerHTML += "<br>s1 == s2: "+(s1 == s2);
let e = getElement(sId); try{ s3Global = Symbol.for('keysame'); s4Global = Symbol.for('keysame'); } catch(ex){ e.innerHTML = ex.toString(); return; } e.innerHTML = "s3 == s4:"+(s3 == s4);
Symbol Use
Immutable unique global symbols provide useful security. I believe it may be very difficult to retrieve the value of any global symbol. Developers can retrieve global symbols by id, however they can't access or modify global symbol values.
Assume you want each person to have a unique identifier that's difficult to obtain. Create a person class.
Create an array of person
.
Create unique symbols in the global registry.
Assign the symbols as unique identifiers for each person
var aryPerson = null; class Person { constructor (lastname,sym,n) { this.lastname = lastname; // attribute, or key, this[sym] // should be inaccessible unless // you have the symbol, 'sym'. // You can't access the value // of 'n' without the value of 'sym'. // You can never access the value of 'sym'. this[sym] = n; } };
Unique Identifiers
Hackers who access JavaScript can't determine the unique ID assigned to each person because they can't read the ID through JavaScript. However, how do you hide the symbol variable itself?
function symbolsSetID(sId){ let e = getElement(sId); try{ sym1 = Symbol.for('p1'); sym2 = Symbol.for('p2'); aryPerson = new Array(); aryPerson.push(new Person('Ford', sym1, 1)); aryPerson.push(new Person('Harrison', sym2, 2)); } catch(ex){ if(e) e.innerHTML = ex.toString(); return; } if(e)e.innerHTML = "Successfully created two symbol IDs."; }
function symbolsGetID(sId){ let e = getElement(sId); if (aryPerson == null){ symbolsSetID(null); } let i = aryPerson.length; for (var j = 0; j < i; j++){ let p = aryPerson[j]; if(p[sym1]){ e.innerHTML += "<br>Found "+p.lastname; } else if(p[sym2]){ e.innerHTML += "<br>Found "+p.lastname; } } }
Persistent Symbols?
Retrieve symbols from the global symbol registry, if they exist. If not, an exception displays. Global symbols may be useful for secure online transactions. The values disappear after use. The values are invisible to hackers even during use.
function getSymbolsGlobalPn(sId,k1,k2){ let e = getElement(sId); let v1,v2 = null; try{ v1 = Symbol.keyFor(k1); v2 = Symbol.keyFor(k2); } catch(ex){ e.innerHTML = ex.toString(); return; } e.innerHTML = "v1: "+v1; e.innerHTML += "<br>v2: "+v2; }
function getSymbolsGlobalPn(sId,k1,k2){ let e = getElement(sId); let v1,v2 = null; try{ v1 = Symbol.keyFor(k1); v2 = Symbol.keyFor(k2); } catch(ex){ e.innerHTML = ex.toString(); return; } e.innerHTML = "v1: "+v1; e.innerHTML += "<br>v2: "+v2; }
Map
Maps are like objects with attribute-value pairs.
However maps allow any JavaScript type as an attribute.
Maps maintain their order. Maps include
a few predefined and unique methods
such as
clear(), delete(), entries(), has(),
forEach(), keys(), set(), forEach(function)
and values()
.
Map Versus Attribute-Value Arrays
JavaScript maps and attribute-value arrays have many similarities. Both include attribute-value pairs. Attribute-value arrays accept only two types of attributes; string or symbol. Map accepts any attribute data type such as functions, objects, string, symbol and other primitives.
Order of Insertion
Map attributes remain in the order they are inserted. With attribute-value arrays, order doesn't matter.
Create Maps
You can create a map in one of two ways.
Either pass an array to the map constructor
or create a new empty map then add
attribute value pairs with mapname.set(attribute,value)
.
The example below creates a map entry with
attribute 3.14
and the value's a function.
We'll apply that in an example.
var mapname = new Map(); mapname.set( 3.14, function(){alert("Hello World."} );
Tap for Map Value Access
var mapname = new Map(); mapname.set( 3.14, function(){alert("Hello World."} ); mapname.get(3.14);
var mapname = new Map(); mapname.set( 3.14, function(){alert("Hello World."} ); var f = mapname.get(3.14); f();
Map Constructor with Array
The following declaration creates a map with an array.
The Map named map1
applies to interactive box examples in this section.
Variable map1
includes three attributes, 'a','b'
and 'c'
.
Map map1
includes three values,
function()...,[1,2,3]
and class c2...
.
let map1 = new Map( [ ['a',function(){s="Yes";}], ['b','[1,2,3]'], ['c','class c2{constructor(){}}'], ] );
Map Values, Entries, Keys
The following three boxes call values(), entries()
and keys()
on the
map1 Map, declared previously.
Method values()
returns just the value
portion of each entry.
Method keys()
returns just the attribute
portion of each entry.
Method entries()
returns both
the attribute and value of each entry.
Map forEach()
The Map.forEach()
method
iterates over every element in the map.
You can access attributes, called keys, values or
entries.
Method forEach()
doesn't save any values.
If you want to maintain values from forEach()
then store them external to the loop.
Map.forEach()
iterates over each element in
a Map, accessing the map, values and keys with a function.
You may pass the function as a parameter, as an arrow function
or inline function.
The next six boxes apply method
forEach()
with different parameters
including a function parameter and an arrow
function parameter.
Map Values, Entries
function mapValues(sId){ let e = getElement(sId); var s1 = ""; for (var value of map1.values()) { s1 += value + "<br>"; } e.innerHTML = s1; }
function mapEntries(sId){ let e = getElement(sId); var sK = ""; for (var ke of map1.entries()) { sK += ke + "<br>"; } e.innerHTML = sK; }
Map Keys, Call External Function
function mapKeys(sId){ let e = getElement(sId); var sK = ""; for (var ke of map1.keys()) { sK += ke + ","; } e.innerHTML = sK; }
var eGlobal = null; function mapForEachFunction(sId){ eGlobal = getElement(sId); eGlobal.innerHTML = ""; map1.forEach(printAttribValue); } function printAttribValue(v,k,m){ eGlobal.innerHTML += "<br><br>"; eGlobal.innerHTML += "attribute: "+k; eGlobal.innerHTML += "<br>value: "+v; }
Map Functions: Inline, Anonymous
function mapForEachInline(sId){ let e = getElement(sId); var sK = ""; map1.forEach( // Inline function function concatKeys(value, key, map){ sK += key +","; } ); e.innerHTML = sK; }
function mapForEachArrow(sId){ let e = getElement(sId); var sK = ""; map1.forEach((value, key, map1)=> { sK += 'key:' + key + ', value:'+value+'<br><br>'; }) e.innerHTML = sK; }
Set
Sets are like arrays except you
can only have one of any particular value.
For example you cannot have a set with two entries
that have a value of a
.
You cannot have duplicate values.
Sets do not include attribute-value pairs. Sets simply contain data entries. Sets have values, not keys (also called attributes).
Create sets with new
and no arguments
or new
and an array argument.
Concatenate sets with the add()
method.
Sets, like Maps, maintain their order.
Entries remain in the same order that
they are added to the set.
Set methods include
clear(), delete(), entries(), has(), add(),
forEach(), set(), forEach(function)
and values()
.
Tap Boxes to See Results
n equals 16
function setCreate(n){ if(set1 == null){ set1 = new Set(); } set1.add(n); }
n equals 25
function setCreate(n){ if(set1 == null){ set1 = new Set(); } set1.add(n); }
Display Sets
function setDisplay(sId){ let e = getElement(sId); if(set1){ for (const v of set1.values()) { e.innerHTML += "<br>"+v; } } else{ e.innerHTML="Tap boxes above to create set1."; }
function setSquareRoot(sId){ let e = getElement(sId); if(set1){ set1.forEach(function(value) { e.innerHTML += "
"+Math.sqrt(value); }) } else{ e.innerHTML="Tap boxes above to create set1."; } }
Summary
You learned about simple and complex JavaScript data types. Sets of colored buttons with JavaScript variables of different types display data about various data types and features. You read about string, number, boolean, null, undefined, class, key-value arrays, simple arrays, functions, Symbol, BigInt, Set and Map types.
Most of the JavaScript source code's in files datatypes.js, symbols.js and p6.js.
Learn JavaScript
JavaScript's the foundation of Web developer and Website design skills. This free and unique JavaScript tutorial includes some new or seldom used, but useful features.