JavaScript Programming Fundamentals
Data Types in JavaScript
| Data Type | Values | Considerations |
|---|---|---|
| String |
Any value with quotes around it, either single, double, or backtick.
|
String objects are probably the object type you will encounter the most often.
Strings exist as primitives and automatically change to objects once a property or method is invoked. Form fields always return their data as strings. An empty string, '' or "" or ``, evalutes to A string with at least one character in it evaluates to |
| Number |
Number values are 64-bit floating points, and there are also special NaN (Not a Number; can occur when doing math that combines a string and a number) and Infinity values. Both of those special values are Number objects.
Numbers never have quotes around them; that would make them a string.
|
Mathematical operations follow the standard PEMDAS (Parentheses, Exponents, Multiplication, Division, Addition, Subtraction) sequence.
Numbers are treated as strings under certain conditions. If you are examining a number to evaluate true/false, JavaScript has an Numbers exist as primitives and automatically change to objects once a property or method is invoked. |
| Boolean |
Only two values exist: true and false
Booleans never have quotes around them; that would make them a string.
|
Booleans exist as primitives and automatically change to objects as necessary. |
| Null |
The only value for this data type is null
This is not quoted; that would make it a string. |
null occurs when the object or variable you are looking for does not exist.
|
| Undefined |
The only value for this data type is undefined
This is not quoted; that would make it a string. |
undefined occurs when a variable is declared (e.g., let sampleVariable;) but has no value.
If a function is expecting a value to be passed to one of its parameters but no value is sent, that parameter is
|
| Symbol | Symbols are assigned a private and unique value. |
Symbols are created via the Symbol() global factory function. These are usually assigned to a variable that is then used as the property of an object, and because symbols are always unique there is no chance of the property name colliding with another property name.
ES6 (ECMAScript Version 6) introduced this primitive. Determining what symbols are assigned as object properties is done via the |
| Object |
Everything else is an object, including functions and nodes returned via the DOM.
If you pass the
|
Objects are unique because when we use them (comparing them, passing them, modifying them) the variable we are acting upon is just a reference to the object and not a copy of the object itself. Changes to that variable cause changes in the object being referenced.
If an object exists, it is If the object does not exist, it is |
Variables
- Variables are named containers for data.
- Variables can be any of the data types (string, number, boolean, null, undefined, symbol, object). If the variable has string data, it is a String object, if it has only number data it is a Number object, etc.
- In the case of the Object data type, the variable is a reference to that object.
- JavaScript is loosely typed, so object types can change automatically under certain conditions:
- When combining a string and a number using
+the number is treated as a string. - Strings used with other mathematical operators (such as multiplication or division) are treated as numbers; if there is a letter or letters in the string the result is
NaN.
- When combining a string and a number using
- Variables are declared (which creates a named memory location) using the
letorconstkeyword, followed by the name you want to use for the variable (see the section on Reserved Words for words to avoid). constis a constant, so the value (if the variable is a primitive) is immutable.- If the variable is not a primitive (for example, if the variable is an Object object), then using
constmeans that it cannot be changed to a different object type (so an Array object cannot be changed to an Object object), but you can modify its properties / methods / values (the options depend on the object type) as much as desired. - Also keep in mind that variables can only start with a letter or an underscore character. An example (the variable is named
x) is:let x;
To declare multiple variables, each can be placed on its own line with
letorconstpreceding its name, or they can share oneletorconstand have commas between the names:let x, y, z;
- To initialize a variable you assign it a value. This is a good thing to do; otherwise the data type is set to
undefinedand that could cause issues later in the code. An example (the value assigned is the number14) is:let x = 14;
- To initialize multiple variables, you can also separate them by commas:
let x = 14, y = 10, z = "yes";
- Only indicate
letorconstwhen initializing or defining the variable. Subsequent uses of that variable just give the variable name. - Variable names are case-sensitive; the same capitalization must be used when the variable is initialized / defined as well as when it is used later.
- All-lowercase or camelCase (capitalizing the first letter of every word after the first word, such as
userNameandvalidateFormData) are recommended. - Do not have spaces in variable names; underscores are fine.
- ES3 (ECMAScript Version 3) does not support
letorconstand instead usesvarin all cases. So if you are seeingvar, the code is using an earlier version of the ECMAScript / JavaScript standard. - In practice,
constis the preferred approach. Code review tools will favor its usage and employers and fellow programmers will expect you to use it, unless the situation calls forlet.
Scope
- ES6 has two scopes:
- Block scope
- Global scope
letandconstboth use block scope, which means they are bound to the innermost block that contains them.- Functions defined with the
functionkeyword are in the global scope, which means they are always available.
Operators
Operators are used in various ways, including combining strings together, performing mathematical operations, and determining whether a specific condition has been met.
| Operator | Usage | Code Example |
|---|---|---|
= |
Simple assignment; not equivalence. |
const greet = 'hello world';
|
+ |
Concatenate; joins together two strings.
Has a different impact when applied to numbers. |
console.log("The value of x is " + x + " in this lecture.");
|
+ |
Addition |
const number1 = 10;
|
+ |
Unary plus - converts operand into a number or NaN if that is not successful. |
let x = "25";
|
- |
Subtraction
Can only be used with numbers. |
const number3 = 10;
|
- |
Unary negation - converts operand into a number or NaN if that is not successful and then negates it. |
let x = "-25";
|
* |
Multiplication
Can only be used with numbers. |
const number5 = 10;
|
/ |
Division
Can only be used with numbers. |
const number7 = 10;
|
% |
Modulo - the remainder after division.
Can only be used with numbers. |
const number9 = 11;
|
++ |
Increment; useful in loops. | See for loops for an example. |
-- |
Decrement; useful in loops. |
for (let i=7; i>0; i--) {
|
+= |
Concatenate-by-value / Add-by-value
For strings: Concatenates the right operand to the end of the left operand. For numbers: Adds the value of the right operand to the left operand. When numbers and strings are both used, the numbers are converted to strings. |
let fullName = "Learning to ";
|
-= |
Compound assignment and subtract-by-value. |
let number11 = 11;
|
*= |
Compound assignment and multiply-by-value. |
let number12 = 15;
|
/= |
Compound assignment and divide-by-value. |
let number13 = 15;
|
%= |
Compound assignment and modulo-by-value. |
let number14 = 16;
|
== |
true if these are equivalent; not assignment
Use |
Each of these is true, which is worrisome:
|
!= |
true if these are not equivalent.
Use |
if (x != 5) {}
|
=== |
true if these are equivalent; no data type conversion occurs. |
See the conditional logic section for examples. |
!== |
true if these are not equivalent and/or not the same data type. |
See the conditional logic section for examples. |
> |
true if left operand is greater than right operand. |
See the conditional logic section for examples. |
>= |
true if left operand is greater than or equal to the right operand. |
See the conditional logic section for examples. |
< |
true if left operand is less than right operand. |
See the conditional logic section for examples. |
<= |
true if left operand is less than or equal to the right operand. |
See the conditional logic section for examples. |
&& |
Logical AND; true if both operands are true. |
See the conditional logic section for examples. |
|| |
Logical OR; true if either operand is true. |
See the conditional logic section for examples. |
! |
Logical NOT; true if the operand is false. |
See the conditional logic section for examples. |
Conditional Logic Using if, else if, and else
- These are conditionals because one result or another will occur if a given condition is met or not met.
- The key determination is whether a test condition evaluates to
trueorfalse.
if Conditionals
- A simple
ifconditional will test whether a single condition is met (if it istrue), in which case the indicated result occurs. - If the condition is not met (if it is
false) then nothing occurs.
Syntax for if Conditional
if (test condition) {
instructions executed if test condition evaluates to true
}
if Conditional Example
const response1 = "yes";
if (response1 === "yes") {
console.log("The answer is " + response1);
}
Note: To evaluate equivalence === is used rather than = (which is for assignment).
Expanding into if - else Conditionals
- In this approach there is also a result if the condition is not met (if the test condition evaluates to
false).
Syntax for if - else Conditional
if (test condition) {
instructions executed if test condition evaluates to true
}
else {
result if test condition evaluates to false; serves well as the default
}
if - else Conditional Example
const response2 = "maybe";
if (response2 === "yes") {
console.log("The answer is " + response2 + ".");
}
else {
console.log("The answer is not yes.");
}
Multiple Branches: if, else if, and else
- In this approach there are a variety of possible outcomes (as many possibilities as are needed).
- As soon as a condition evaluates to
true, the conditional sequence is exited (subsequent conditionals are not checked). - Having the
elseis optional.
Syntax for Multiple Branches
if (first test condition) {
instructions executed if first test condition is true
}
else if (second test condition) {
next to be evaluated if first test condition was false;
stops here and executes this code if second test condition is true
}
else if (third test condition) {
next to be evaluated if second test condition was false;
stops here and executes this code if third test condition is true
}
else {
result if all other test conditions are false
}
Multiple Branches Example
const response3 = "no";
if (response3 === "yes") {
console.log("The answer is yes.");
}
else if (response3 === "maybe") {
console.log("The answer is maybe.");
}
else if (response3 === "no") {
console.log("The answer is no.");
}
else {
console.log("The answer is unknown.");
}
Nested Conditionals
- Conditionals can also have other conditionals nested inside them.
- This has the most potential for syntax errors (usually due to missing brackets).
Syntax for Nested Conditionals
if (first test condition) {
if (second test condition) {
result only occurs if both first and second test conditions are true
}
else if (third test condition) {
result only occurs if first test condition is true, second test condition is false, and third test condition is true
}
else {
result only occurs if first test condition is true and all subsequent test conditions are false
}
}
Nested Conditionals Example
const answer1 = 25;
if ((answer1 > 10) && (answer1 < 30)) {
if (answer1 <= 15) {
console.log("The first answer is less than or equal to 15.");
}
else if (answer1 <= 20) {
console.log("The first answer is less than or equal to 20.");
}
else {
console.log("The first answer is between 20 and 30.");
}
}
Considerations with Boolean Operators
- JavaScript uses short-circuit evaluation when dealing with boolean / logical operators.
- In
&&(AND) setups if the first operand isfalsethen the second operand is ignored, because the overall result would have to befalseregardless of how subsequent operands evaluate.- This is also the case for
(a && b && c). - If
aisfalsethen the others would not be evaluated. - If
aistrueandbisfalse, thencis not evaluated.
- This is also the case for
- In the case of
||(OR), if the first operand istruethen the overall result istrue, so the additional operands are not evaluated.- One interesting variation for
||is in the area of assignment. - If you have
const x = a || b;and theavariable evaluates tofalse, thenxis automatically assigned to the value of thebvariable.
- One interesting variation for
The Ternary Operator
- The ternary operator offers a more streamlined approach to
if - elsesetups. - The ternary operator uses a
?to separate a condition to be evaluated from two outcomes, which are separated by the:character.
Syntax for Ternary Operator
// code execution (expression) ? true expression result : false expression result; // assignment const x = (expression) ? value if expression is true : value if expression is false;
The ternary (triple) name comes from the three sections:
- Section preceding the
? - Section between
?and: - Section to the right of
:
Ternary Operator Example 1: Assignment
const ternary_ex1 = true; const ternary_result1 = (ternary_ex1) ? 'it was true' : 'it was false'; console.log(ternary_result1);
Ternary Operator Example 2: Code Execution
const ternary_ex2 = 0;
ternary_ex2 ? console.log('it was true') : console.log('it was false');
The switch() Control Structure
- While the ternary operator works well for a straightforward true/false (if/else) situation, the switch control structure is ideal as a replacement for
if / else if / else if / else if / else if / else if / else if / else if / elsesituations. - In other words, it is ideal for lengthy conditional logic situations.
- The parameter passed to
switchis the variable whose value is being checked. - Each condition you are checking (each value you are checking) is a
case. - An optional
defaultcan be specified; this is equivalent toelsebecause it is what happens if none of thecasevalues match. - The use of
breaksaves time by stopping the rest of thecasechecking after that point. - Strict equivalence (
===) is used when matching value to case, so keep that in mind.
switch() Code Example
const the_answer = 'yes';
switch (the_answer) {
case 'no' : console.log('Not quite'); break;
case 'maybe' : console.log('Getting closer'); break;
case 'yes' : console.log('Correct!'); break;
default : console.log('Nowhere close');
}
Array Objects
- While regular variables can only hold one value at a time (most languages refer to these as scalar variables), an array object is a special type of variable that can hold multiple values. Any data type can be stored.
- By default, the content within an array is indexed (organized) numerically and the sequence always begins with the number
0(position0is the first item in the array). - There is no upper bound to the numerical index.
- It is possible to index based on strings and to have multi-dimensional (nested) arrays, which is covered in a separate lecture.
- If you skip some of the positions in the sequence (not giving those positions a value) they have the
undefineddata type. - JavaScript supports dynamic array addressing, which means that arrays can be increased or decreased in size at any time.
- There are multiple ways to create and populate Array objects; we will focus on the most efficient way (referred to as the array literal). An example is:
const topics = ['Topic 1', 'Topic 2'];
- Accessing / adding / editing items in the array continues to use the
[]syntax. - If we wanted to add 'Topic 3' we would specify:
topics[2] = 'Topic 3';
- Array objects have a
lengthproperty that reflects the highest numerical index assigned. Non-numerical indexes do not count in thelength. - There are a variety of Array object methods covered in a separate article.
for and for-of Loops
- A
forloop executes a piece of code repeatedly, until a pre-set condition evaluates tofalse. - These are extremely useful when iterating through all the items in an array object and typically use the
lengthof the array to determine when to stop. This works just fine in most situations.
for loop Syntax
for (initial value; test condition; update value) {
JavaScript statements to execute in each loop
}
for Loop Code Example
console.log('HTML Headings from Largest to Smallest');
for (let size = 1; size < 7; size++) {
console.log('<h' + size + '>');
}
for Loop Considerations
- Always make sure that the code within the loop is moving toward the point where the test condition is
false. Usually this involves incrementing (++) or decrementing (--) a value. If this is not being done you very likely have an infinite loop. - These loops run faster when the test condition is a fixed value, such as a set number. Otherwise each time the loop occurs the value needs to be calculated / checked again. If there are thousands of items in the array this can make a noticeable impact on performance.
- If you are looping through an array, and your loops are not modifying the number of items in the array, then assigning the length of the array to a variable and checking against that number will speed up the loop performance.
for-of Loops
- With ES6 we also have a
for-ofloop. - This loop variation eliminates the need for a counter or something else to evaluate true/false, in order for the loop to continue. Instead this goes through every item in the array (or other collection / list).
- It still supports
breakto leave the loop andcontinueto jump to the next loop iteration.
for-of loop Syntax
for (drop-in variable of dataset) {
JavaScript statements to execute in each loop
}
for-of Loop Code Examples
console.log('HTML Headings from Largest to Smallest');
for (const size of ['1', '2', '3', '4', '5', '6']) {
console.log('<h' + size + '>');
}
If you need the index of the array element, there is also an entries() method:
console.log('HTML Headings from Largest to Smallest');
for (const [index, size] of ['1', '2', '3', '4', '5', '6'].entries()) {
console.log(index + ': <h' + size + '>');
}
Object Literals
- The object literal syntax establishes a new Object object with the variable name you indicate.
- Properties and methods (along with their values) are represented in a name : value pattern, with each one (except for the last name / value pair) being followed by a comma.
- The object literal syntax uses the
{}(curly braces), which distinguishes it from other literals, such as the array literal that uses[](square braces). - What makes the object literal so useful is that it prevents the global namespace from being cluttered with too many variable names (which raises the chances of a collision between different sets of code). Here is a quick example:
const sampleObject = { maxValue : 14, welcome : 'Hello world', doSomething : function() {}, theData : [3,5,7,8] } - To call the
doSomething()method is as simple as specifyingsampleObject.doSomething(); - The array object is accessed via
sampleObject.theData[0](or whatever position you wish to access). - The only potential collision in the global namespace is with
sampleObject, so hopefully no other code being called for the page used that name for one of their global variables. - Edits to the Object (including adding methods / properties) can be made from anywhere in the code, such as outside of the object literal:
sampleObject.farewell = 'Goodbye world';
- There is also a delete method in JavaScript that is used to remove properties and methods, such as:
delete sampleObject.maxValue;
Functions
- Functions typically contain multiple JavaScript statements (although they could just contain one statement) and can be passed parameters (also called arguments), which then modify the outcome.
- Parameters are block scope variables that are automatically declared when the function is called.
- Functions are defined once and can be called an unlimited number of times.
- Functions can call other functions and functions can even be passed to other functions as a parameter. This is important; you will often want one function to pass data to another function or to trigger multiple other functions.
- Functions can also be nested. Nested functions can access the block scope variables of their parent function, but the parent function cannot access the block scope variables of the child function. This is referred to as a closure.
- Like variables, the name given to a function is case-sensitive so the same capitalization must always be used. All-lowercase or camelCase style are recommended.
- The basic syntax for a function (items that would need to be defined are in italics) is:
function name() { JavaScript statements } - Note that the
()following the name can be left empty if no values are passed to the function. If values were passed the parameter name(s) would be noted in the parentheses; multiple parameters would be separated by commas. - Regardless of the parameters you pass to the function, every function is also passed two additional parameters:
arguments[]- This array-like object contains every parameter you passed, regardless of whether the function was expecting to receive it. This object has alengthproperty that can be used in aforloop that cycles through all the parameters passed (the first item isarguments[0]).this- A reference to the object that called the function
Function Code Example
function tracker(author, topic) {
console.log(topic + ' article by ' + author + '.');
}
tracker('Jason W', 'JavaScript');
Additional Function Details and Variations
- Functions always return (send back) a value, which is
undefinedunless you set it. You can also specify thereturnkeyword, followed by the value being sent back. Be sure to start this value on the same line asreturn. - If you simply want to stop the function, you can specify
return;at any point in the code. - Within the context of a custom Object that we create, these functions are the methods of that object.
- In ES6 we can assign default values to parameters; these are used if a value is not passed.
- ES6 also introduced arrow functions.
- Arrow functions change what
thisreferences inside the function. In an arrow function,thisalways references the object containing the function. - Outside of arrow functions,
thisrefers to whatever invoked the function.
- Arrow functions change what
Function with Defaults Example
function tracker(author='JW', topic='Coding') {
console.log(topic + ' article by ' + author + '.');
}
tracker();
Arrow Function Example
const tracker = (author='JW', topic='Coding') => {
console.log(topic + ' article by ' + author + '.');
}
tracker();
Classes
- Classes in ES6 provide the standard object-oriented experience familiar to other object-oriented languages.
- They are defined with the
classkeyword, contain aconstructormethod, other methods, and are invoked using thenewkeyword. - ES6 classes also support inheritance (one class can extend another class), but only one level and one parent. The
superkeyword is used to access the parent class. - Read more about ES6 classes
class Code Example
class Tracker {
constructor(author, topic) {
this.author = author;
this.topic = topic;
}
output() {
return this.topic + ' article by ' + this.author + '.'
}
}
const articleTrack = new Tracker('Jason W', 'JavaScript');
console.log(articleTrack.output());
Template Literals
- Template literals are another option in ES6 for working with strings.
- They are enclosed in backticks.
- These can be much easier to read (and write) than a concatenated sequence, because
${}indicates what to interpolate.
Template Literal Code Example
const piVal = 3.14;
const str = `The first 3 digits of pi are ${piVal}`;
console.log(str);
Troubleshooting Tools & Techniques
One of the realities of programming is that errors will occur. Thankfully, there are a variety of options available for troubleshooting these errors.
Developer Tools Console
- Press F12 when your browser window has focus to launch the Developer Tools. It will have a tab for 'Console'.
- Errors pile up in the console, so be sure to clear it before you run your code. You do not want to spend hours trying to track down an old error that has long since been fixed.
console.log()
- As seen in the examples, this is an unobtrusive way to output values for troubleshooting purposes.
- Reload the console before each run, if you're troubleshooting the same code.
alert()
- By inserting
alert()statements at various points in your code, you can determine what value a variable has at a given point, or even just determine that a function is being called. - If your
alert()never shows, you know you have a problem with that function getting called.
Web Developer Toolbar
- This is a Firefox, Chrome, Edge, and Opera extension. The extension has a variety of useful tools, including the ability to disable JavaScript entirely and also to quickly bring up the JavaScript console (and alert you that errors have occurred).
- The toolbar can be downloaded from https://chrispederick.com/
try/catch
try/catchis used in both code debugging and also in production code to recover gracefully from errors (and perhaps branch code in a different direction).- If the code inside of
try {}throws any error, that error is passed to thecatch() {}portion and the code inside ofcatch() {}executes. If no error occurred, thecatch() {}is skipped. - Consider the following
try/catchexample, which throws an error because===is used rather than=and the variable is not defined:try { myVariable === true; } catch(error) { alert(error); }
Reserved Words
- Reserved words should not be used for variables, functions / methods, or properties since they have other meanings in JavaScript (or have been reserved for future use).
- Variations (even slight changes) will make the word permissible.
- If a special syntax style is used the reserved word can be used, but we won't worry about that.
- To quickly search for a word in the list, use Control + F ('Find in Page')
- The reserved words are:
abstract, boolean, break, byte, case, catch, char, class, const, continue, debugger, default, delete, do, double, else, enum, export, extends, final, finally, float, for, function, goto, if, implements, import, in, instanceof, int, interface, long, native, new, package, private, protected, public, return, short, static, super, switch, synchronized, this, throw, throws, transient, try, typeof, var, void, volatile, while, with - Also avoid words used for core objects (such as Array), properties, or methods, or using
null,undefined,true, orfalsedue to confusion with data types. Even if there are no errors, you will almost certainly confuse the next person to work with your code.
Strict Mode
- Strict mode is triggered by specifying either:
'use strict';
or
at the start of your JavaScript file, before any other statements."use strict";
- That will put the entire file into strict mode. Alternately, this can be the first instruction inside a function to put that function into strict mode.
- This mode causes more errors to show in the console (helpful for improving code quality) and can also improve performance.
- Read more about strict mode