Basics

JavaScript is a coding language that defines the behavior of web pages.

Languages

JavaScript is a programming language. It is usually used with HTML and CSS, which are not programming languages. The difference is that a programming language defines a process, while a markup language adds context and structure to text.

A programming language is a formal language capable of expressing computable functions. More simply, with JavaScript you can use branching and loops to define a complex process.

A markup language is a formal language that adds structure to normal text. So with HTML alone, you can only define static web pages. HTML describes the data. It contains no logic.

Template

Empty template for an HTML document that includes HTML, CSS, and JavaScript. Save text as filename.html.


<html>
    <head>
        <!-- head tags here -->
        <style type="text/css">
            /*css here*/
        </style>
    </head>
    <body>
        <!-- page contents here -->
    </body>
</html>

<script type="text/javascript">
    //javascript here
</script>

Internal

JavaScript can also be placed in the head element, but that is not standard practice.


<html>
    <head>
        <script type="text/javascript">
            //javascript here
        </script>
    </head>
    <body>
    </body>
</html>

You can also put the JavaScript element directly in the body of the HTML. This is not standard practice. If you do follow this model, place the script tag after all other content so that compiling the JavaScript does not slow down display.


<html>
    <body>
        <script type="text/javascript">
            //javascript here
        </script>
    </body>
</html>

External

The convention is to only put references to external JavaScript files in the head.

External javascript files improve loading speeds because they can be cached. They separate the scripts from the HTML which makes both easier to read and maintain, and allows the scripts to be reused by other HTML pages.

myHTML.html

<html>
    <head>
        <script type="text/javascript" src="myExternalScripts.js"></script>
        <script type="text/javascript" src="http://a.really/specific/path.js"></script>
    </head>
    <body>
        <div id="main">
        </div>
    </body>
</html>

myExternalScripts.js

document.getElementById("main").innerHTML = "JavaScript test";
Syntax

Comments


//comments

/*
multiline comments
*/

Variables

JavaScript is loosely typed, meaning all variables have type 'var' and any value can be assigned to any variable.


var x, y, z;
x = 5;
y = "text";
z = [a, "b", 3];
x = y;

Declare several variables in one statement

var x, y, z;

Define several variables in one statement

var x = 5, y = "text", z = [a, "b", 3];

Re-declaring a variable does not change the value - still, don't do this

var x = 5;
var x;

Literals

Numbers

var x = 10.50;
var y = 1001;

Strings can be surrounded by single or double quotes.

var x = "string";
var y = 'string';
var z = "inner ' ' quotes without escape characters";

Expression

An expression is any combination of values, variables, and operators that compute to a single value.

The computation is called an evaluation.

Ex: The expression "5 * 10" evaluates to "50".

Identifiers

Identifiers include variable names, function names, labels, and keywords.

The first character must be a letter, underscore (_), or dollar sign ($). Usually, start with a letter.
Other characters may be letters, digits, underscores, or dollar signs.

Identifiers are case-sensitive.

By convention, JavaScript identifiers are written in lower camel case. The first character is lowercase, the first character of each subsequent word is uppercase, and there are no spaces or underscores between words.

var inputLastName;
var _countContents;

By convention, JavaScript global variables are named in all uppercase.

By convention, JavaScript constants are named in all uppercase.

Statement

Each JavaScript statement ends with a semicolon (;). You can put multiple statements on a single line, but by convention it is usually one line per statement.


var x = 5;
var y = 6; var z = x + y; console.log(z);

A statement can stretch across multiple lines.

Whitespace

JavaScript ignores extra whitespaces. Use them to make your code more legible.

Code Block

JavaScript defines code blocks with { curly braces }.

The convention in JavaScript is to use Egyptian style braces.

Roman

function Display()
{
    //code here
}

Egyptian

function Display() {
    //code here
}

Usually code blocks are functions, or loops, or switch statements.
You can also define named code blocks anywhere you want.
To use breaks in this case, you must specify the code block's label.

var x = ["a", "b", "c", "d"];
var text = "";
myLabel: {
    text += x[0];
    text += x[1];
    break myLabel;
    text += x[2];
    text += x[3];
}
console.log(text); //"ab"

Keyword

aka Reserved Word

Keywords are special words with meaning in the programming language. You cannot use these words as variable names or function names.

Ex: break, continue, do, while, for, function, if, else, return, switch, try, catch, var

Undefined

Undefined

'undefined' is a reserved word. The type of 'undefined' is 'undefined'.

Variables that have been declared but not defined have value 'undefined'.


if(x == undefined) {}

y = undefined;

Null

'null' is a reserved word. It is considered an object. 'null' is not the same as 'undefined'.


var y = null;

null !== undefined
null == undefined
DOM

DOM stands for Document Object Model. All major browsers support DOM.

The document object model is an object view of the xml structure of the webpage. <HTML> is the root object, and all other nodes are children of the root, in the tree structure defined by the XML.

HTMLCollection

Queries like "document.getElementsByTagName('div')" return HTMLCollections, which are live collections that update as you update the DOM.

A NodeList object is a collection of DOM elements that may or may not be live.

To convert either an HTMLCollection or NodeList to an array (which will not be live):

var collection = document.getElementsByTagName('div');
var array = Array.from(collection);
//or
var array = [...collection];

Elements

Lookup element

document.getElementById('id'); //element
document.getElementsByName('name'); //array
document.getElementsByClassName('class'); //array
document.getElementsByTagName('tag'); //array

Create element

var element = document.createElement('div');

Loop through children:

for(var i=0; i<element.children.length; i++)
{
    var child = element.children[i];
}

Add child:

element.appendChild(childElement);
element.insertBefore(childElement, siblingElement);
element.insertBefore(childElement, element.firstChild);

Add child after element:

element.parentNode.insertBefore(childElement, element.nextSibling);

Navigate:

var nextSiblingNode = element.nextSibling(); //could be an element, or text, or comment
var nextSiblingElement = element.nextElementSibling(); //just elements

var previousSiblingNode = element.previousSibling();
var previousSiblingElement = element.previousElementSibling();

Window

"window" is a variable made available to JavaScript by the browser. It gives you access to the current browser window.

Show an alert popup

window.alert("An error");

Console

"console" is a variable made available to JavaScript by the browser. It gives you access to the browser's console window.


console.log("A message");

Document

"document" is a variable made available to JavaScript by the browser. It gives you access to the DOM for your webpage.


var myElement = document.getElementById("myId"); //references HTML tag with id="myId"

Append HTML to the end of your document. This content will be interpreted as HTML.

document.write("some more text<br/>more text on lower line");
Warning: using document.write after the DOM has fully loaded will cause it to overwrite the whole DOM. Subsequent uses will still append.

Editing Elements

Editing element inner html:

var myElement = document.getElementById("myId");
myElement.innerHTML = "Edited html";

Editing element attributes:

var myImage = document.getElementById("myImage");
myImage.src = "anotherImage.png";

Editing element styles:

var myDiv = document.getElementById("myDiv");
myDiv.style.fontSize = "25px";

Editing classes

element.classList.add('class');
element.classList.remove('class');

Quickly replace all classes on an element (much faster than removing them individually from the list)

element.className = "class";

Form


var myForm = document.forms["myFormName"];
var myField = myForm["myFieldName"];
var myFieldValue = myField.value;

myFieldValue = document.forms["myFormName"]["myFieldName"].value;

Form validation example. The form won't actually submit until the validation passes.

<form name="myForm" onsubmit="return validateForm()" action="submitForm.php" method="post">
    Name: <input type="text" name="firstName" />
    <input type="submit" value="Submit" />
</form>

<script>
function validateForm() {
    var firstName = document.forms["myForm"]["firstName"].value;
    if(firstName == "") {
        alert("Name is required.");
        return false;
    }
    return true;
}
</script>

Data Attributes

You can add any custom attribute to an HTML element.
By convention, use the prefix "data-" to name custom attributes.

<input id='address' data-address-type='billing' />

Custom attributes with the prefix "data-" can be accessed through javascript.

var addressType = document.getElementById('address').dataset.addressType;

Object

Include an entire other html page within your html page:

<html>
    <body>
        <object type="text/html" data="otherFileA.html"></object>
        <object type="text/html" data="otherFileB.html"></object>
    </body>
</html>

The other files will display in their own frame.

Access the DOM of those other files:

var objects = document.getElementsByTagName("object");
for(var i = 0; i < objects.length; i++)
{
    var object = objects[i];
    var objectDocument = object.contentDocument;
    //access "objectDocument" just like local "document"
}
Operators

Assignment


var y = 5;
var x = y;

x += 10; //x = x + 10;
x -= 5; //x = x - 5;
x *= 2; //x = x * 2;
x /= 4; //x = x / 4;
x %= 10; //x = x % 10;

Arithmetic


var x = (5 + 6) * 10 / (2 - 4) % 10;
* and / have higher precedence than + and -
Use ( parentheses ) to control precedence.

Increment, Decrement


x++;
++x;

y--;
--y;

Concatenation


var x = "Hello " + name + "."; //Hello John.
var y = "5" + 2 + 3; //523
var z = "2 + 3 + "5"; //55

Comparison


if(x == y) {} //values are equal

var x = "John";
var y = new String("John");
if(x == y) {} //true because they have the same value

var z = new String("John");
if(y == z) {} //false because objects are compared by reference, and these are different instances
if(y === z) {} //still false, because the objects are different instances


if(x === y) {} //values and types are equal

if(x != y) {} //values are not equal

if(x !== y) {} //values or types are not equal

if(x > y) {} //greater than

if(x < y) {} //less than

if(x >= y) {} //greater than or equal to

if(x <= y) {} //less than or equal to

z = (x > y) ? 5 : 10; //ternary operator - shorthand for if/else

Logical


if(a && b) {} //a AND b

if(a || b) {} //a OR b

if(!a) {} //NOT a

Type


if(typeof(x) == String) {} //the type of the variable

if(x instanceof String) {} //true if x is of type string

typeof("") == "string"
typeof(0) == "number"
typeof(NaN) == "number"
typeof(Infinity) == "number"
typeof(true) == "boolean"
typeof({}) == "object"
typeof([]) == "object"
typeof(null) == "object"
typeof(undefined) == "undefined"
typeof(function myFunc() {}) == "function"

Bitwise


flag = (a & b); //AND the bit-values together

flag = (a | b); //OR the bit-values together

flag = (!a); //NOT the bit-values

flag = (a ^ b); //XOR the bit-values together

flag = (a << b); //zero-fill left shift a by b

flag = (a << b); //signed right shift a by b

flag = (a >>> b); //zero-fill right shift a by b

In


if("property" in myObject)
    return myObject.property; //or return object[property];
if(2 in myArray)
    return myArray[2];
if(!("property" in myObjects))
    return null;
Variables

Hoisting

Variable declarations are hoisted to the top of the current scope, which means either (A) the top of the enclosing function or (B) the global scope.
Therefore you can use a variable before (earlier in the file) it is declared.

Variables declared with "let" or "const" keywords are NOT hoisted.

console.log(x); //outputs ReferenceError: can't access lexical declaration 'x' before initialization
let x = 1;

console.log(y); //outputs ReferenceError: can't access lexical declaration 'y' before initialization
const y = 2;

Variable initializations are NOT hoisted.

console.log(x); //outputs undefined
var x = 5;

Standard practice is to declare all variables at the top of their scope, regardless of hoisting.

Scope

Global scope is available to every part of the script.
Variables declared outside any function or object have global scope.

Function scope is available to all statements within the function.
Variables declared within a function have function scope, also called local scope.

Block scope is available to all statements occurring later within the same block.
A code block is anything enclosed in { }.
Only variables declared as "let" or "const" have block scope.

{
    let x = 5;
}
console.log(x); //outputs ReferenceError: x is not defined

{
    const y = 6;
}
console.log(y); //outputs ReferenceError: y is not defined

Variables declared as "var" cannot have block scope, even when declared inside a block:

{
    var x = 5;
}
console.log(x); //outputs 5

It is called "redeclaring" if you declare a "var" variable both inside and outside a block. Within just { } it does not make a difference.

var x = 5;
{
    var x;
    console.log(x); //outputs 5
}
console.log(x); //outputs 5

Unless you reinitialize the variable, then it will change the global value.

var x = 5;
{
    console.log(x); //outputs 5
    var x = 6;
    console.log(x); //outputs 6
}
console.log(x); //outputs 6

I don't know the difference between the previous examples and this one, but the behavior is different when you using the grouping operator ( ).
If you don't declare the variable in the inner scope, the global is used.
If you do declare the variable in the inner scope, it is like a whole different variable and the outer variable's value is maintained even if the inner one changes.

var x = 5;

(function() {
    console.log(x); //outputs 5
    x = 2;
    console.log(x); //outputs 2
})();

console.log(x); //outputs 2

(function() {
    console.log(x); //outputs undefined because the variable declaration was hoisted
    var x = 8;
    console.log(x); //outputs 8
})();

console.log(x); //outputs 2

Declaring a "let" or "const" variable will not affect a global "var" variable of the same name.

var x = 5;
var y = 6;
{
    let x = 1;
    const y = 2;
}
console.log(x); //outputs 5
console.log(y); //outputs 6

You can also nest "let" and "const" variables to reuse the variable names.

let x = 5;
{
    let x = 6;
    console.log(x); //outputs 6
}
console.log(x); //outputs 5

The value of "let" variables can be changed.
The value of "const" variables cannot be changed.

Container

A container is a collection of data with no methods/behavior. For example, arrays are containers.

Strictly speaking, containers are supposed to be immutable. By that definition, arrays are not containers because you can add/remove elements and edit elements.

A "functor" is a container that can be mapped onto by an unary function (a function that accepts one parameter). The function will be applied to each element of the functor, and a container of the results of each function call will be returned. For example, array.map(function).

An implementation of "map" is expected to return the same container type as the container it is operating on. Thus, you can chain "map" calls.

Number

A data type


var x = 5;
var y = 10.50;
var z = 120e5; //exponential (scientific) notation

Storage

(link to WWC floating point lesson for much more details)

All numbers are stored as 64-bit floating points.
Bits 0-51 store the number, bits 52-62 store the exponent, and bit 63 stores the sign.

Integers are accurate up to 15 digits.

The maximum number of decimals is 17.

Floating point arithmetic is not completely accurate.

var x = 999999999999999; //999999999999999
var y = 9999999999999999; //10000000000000000

var z = 0.2 + 0.1; //0.30000000000000004

Therefore, use some form of rounding

var z = (2 + 1) / 10; //0.3

Constants

Number.MAX_VALUE
Number.MIN_VALUE
Number.NEGATIVE_INFINITY
Number.NaN
Number.POSITIVE_INFINITY

NaN

NaN stands for Not A Number. This result is given when an operation results in an illegal value.

var x = 100 / "Apple"; //NaN
if(isNaN(x)) {} //true

Any operations that includes a NaN will result in NaN. Except some weird ones.

var x = NaN + "5"; //"NaN5"

Infinity

'Inifity' or '-Infinity' will be returned if you go beyond the range of JavaScript numbers.

if(2 == Infinity) {} //false
if(2/0 == Infinity) {} //true

Binary

Numbers prefixed with "0b" are interpreted as binary integers.

var x = 0b10001; //17

Octal

Some browsers interpret numerics with a leading zero as octals.

var x = 017; //15

Hexadecimal

Numerics with prefix '0x' are interpreted as hexadecimal values.

var x = 0xFF; //255

Conversions

Number

var a = Number(true); //1
var b = Number(false); //0
var c = Number("10"); //10
var d = Number("10 20"); //NaN
var e = Number(Date()); //returns number of milliseconds since 1970/1/1

parseInt only works on strings, and only returns the first integer found

var a = parseInt("10"); //10
var b = parseInt("10.33"); //10
var c = parseInt("10 20 30"); //10
var d = parseInt("in 10 years"); //10
var e = parseInt("no numbers"); //NaN

parseInt defaults to base-10, but you can also specify the base

var f = parseInt("0110110001", 2);

parseFloat only works on strings, and only returns the first number found

var a = parseFloat("10"); //10
var b = parseFloat("10.33"); //10.33
var c = parseFloat("10.20.30"); //10.20
var d = parseFloat("in 10 years"); //10
var e = parseFloat("no numbers"); //NaN

ToString

number.toString(base) returns string


var x = 128.toString(); //"128"
var y = 128.toString(16); //"80" 


var s = String.fromCharCode(asciiCode);

ToExponential

number.toExponential(decimalPlaces) returns string


var x = (9.656).toExponential(2); //"9.66e+0"
var y = (9.656).toExponential(4); //"9.6560e+0"

ToFixed

Rounds a number to the specified decimal places.

number.toFixed(decimalPlaces) returns string


var x = (9.656).toFixed(0); //"10"
var y = (9.656).toFixed(2); //"9.66"
var z = (9.656).toFixed(4); //"9.6560"

ToPrecision

Rounds a number to the specified number of digits.

number.toPrecision(digits) returns string


var x = (9.656).toPrecision(); //"9.656"
var y = (9.656).toPrecision(2); //"9.7"
var z = (9.656).toPrecision(5); //"9.6560"

String

A data type


var x = "text";
var y = 'text';

Strings are immutable, they cannot be edited, only replaced. Thus, all string methods return a string.

Conversions

Adding a Number and a String will result in the Number being treated as a String.

var x = "Summation: " + 3 + 5; //"Summation: 35"

JavaScript will attempt to convert Strings to Numbers in specifically numeric operations.

var x = "100" / "20"; //5

Escape Character

Escape character \

var x = "with \"double quotes\" included";
\" double quote
\' single quote
\\ backslash

Outdated escape characters, not really applicable to the web:
\b backspace
\r carriage return
\f form feed
\t tab horizontal
\v tab vertical

Some browser support using a single slash to type text across multiple lines

var x = "skjfhksdfuhskfushkdfusdf\
skdfjshkfusdfhkusdf";

Template Literal

Template literals are string literals with embedded expressions.
Template literals are surrounded by backticks.


var noun = "cat";
var text = `I got a new ${noun} yesterday.`; //I got a new cat yesterday.

var a = 3;
var b = 5:
text = `${a} + ${b} = ${a+b}`; //3 + 5 = 8

Tagged literals:

function myTag(stringArray, expression1, expression2)
{
    //stringArray = ['', ' is ', ' than me.']
    //expression1 = 'Bob'
    //expression2 = 35
    
    var adjective = (expression2 < 30) ? "younger" : "older";

    return expression1 + stringArray[1] + adjective + stringArray[2];
}

var name = "Bob";
var age = 35;
console.log(myTag`${name} is ${age} than me.`); //prints 'Bob is older than me.'
Tagged literals can return any data type, not just a string.

You can access raw strings in tagged literals (strings without special characters interpreted):

function myTag(stringArray)
{
    console.log(stringArray.raw[0]); //prints the '\n' instead of an endline
}
myTag`text line 1\ntext line 2`;

Raw strings can also be written this way:

console.log(String.raw`text line 1\ntext line 2`);

Operations

Length

var x = "text";
var a = x.length; //4

IndexOf
string.indexOf(text) returns int
string.lastIndexOf(text) returns int

var x = "text";
var a = x.indexOf("t"); //0
var b = x.indexOf("ex"); //1
var c = x.indexOf("b"); //-1
var d = x.indexOf("t", 2); //start looking at index 2, so result is 3


var x = "text";
var a = x.lastIndexOf("t"); //3
var b = x.lastIndexOf("t", 2); //start looking at index 2, so result is 0

Search
Similar to indexOf, but more powerful. Can search for regular expressions.
Does not accept a starting index.

Slice
string.slice(startIndexInclusive, endIndexExclusive) returns string

var x = "The quick brown dog...";
var a = x.slice(4, 8); //"quic"
var b = x.slice(-18, -14); //counts from end of string, so "quic"
var c = x.slice(4); //goes to end of string, so "quick brown dog..."

Substring
Like slice, but cannot accept negative indexes.

Substr
string.substr(startIndexInclusive, length) returns string

var x = "The quick brown dog...";
var a = x.substr(4, 8); //"quick br"
var b = x.substr(-10, 8); //counts from end of string, so "quick br"
var c = x.substr(4); //goes to end of string, so "quick brown dog..."

Replace
Replaces the (default) first matching substring found.
Accepts regular expressions.
Is case-sensitive.
string.replace(search, replacement) returns string

var x = "The dog is a dog";
var y = x.replace("dog", "cat"); //"The cat is a dog"

Replace all

var x = "The dog is a dog";
var y = x.replace(/dog/g, "cat"); //"The cat is a cat"

ToUpperCase
string.toUpperCase() returns string

ToLowerCase
string.toLowerCase() returns string

Concat
string.concat(stringA, stringB, ...) returns string

var x = "Hello".concat(" World"); //"Hello World"
var y = "Hello".concat(" ", "Wo", "rld"); //"Hellow World"

CharAt
string.charAt(index) returns string
string.charCodeAt(index) returns int

var x = "Hello".charAt(0); //"H"
var y = "Hello".charCodeAt(0); //72

Accessing strings as if they were arrays is not supported by all browers. You cannot edit a string this way.

var x = "Hello"[0];

Split
Convert a string into an array of strings.
string.split(seperator) returns []

var x = "Hello".split(""); //["H", "e", "l", "l", "o"]
var y = "a,b,c".split(","); //["a", "b", "c"]
var z = "Name|Address|Phone".split("|"); //["Name", "Address", "Phone"]
var a = "Hello".split(); //["Hello"]

Includes
string contains the substring

var containsSubstring = "abcdef".includes("cd");
Boolean

A data type


var x = true;
var y = false;

Values

Anything with a real value will evaluate to true.
Ex: 100, -3.14, "word", "false", true

Anything without a real value will evaluate to false.
Ex: 0, -0, "", undefined, false, NaN
Object

A reference data type. In Javascript, they are structurally similar to maps/dictionaries.

Objects are made up of properties and methods.
- Properties are name:value pairs.
- Methods are name:function pairs.

Declaration

Object literal:

var obj = { 
    firstName: "John", 
    lastName: "Smith",
    fullName: function() { return firstName + " " + lastName; }
};

Object constructor:

var obj = new Object();
obj.firstName = "John";
obj.lastName = "Smith";
obj.fullName = function() { return firstName + " " + lastName; };

Reusable object definition:

function objFactory(lastName, firstName) 
{
    return {
        firstName: firstName, 
        lastName: lastName,
        fullName: function() { return firstName + " " + lastName; }
    };
}
var obj = objFactory("Smith", "John");
console.log(obj.fullName()); //outputs "John Smith"

Constructor Design Pattern:

function MyObject(lastName, firstName) 
{
    this.lastName = lastName;
    this.firstName = firstName;
    this.fullName = function() { return firstName + " " + lastName; };
}

var obj = new MyObject("Smith", "John");
console.log(obj.fullName()); //outputs "John Smith"

Create from prototype:

var myPrototype = {
    firstName: "John", 
    lastName: "Smith",
    fullName: function() { return this.firstName + " " + this.lastName; }
};
var myCustomer = Object.create(myPrototype);
console.log(myCustomer.fullName()); //outputs "John Smith"

Properties


var x = obj.lastName;
var y = obj["lastName"];

Property names can be strings. You can reference string indexes with dot notation or bracket notation.

var a = { firstName: "John" };
console.log(a.firstName); //outputs "John"
console.log(a["firstName"]); //outputs "John"

var b = { "firstName": "John" };
console.log(b.firstName); //outputs "John"
console.log(b["firstName"]); //outputs "John"

Property names can be numbers. To reference number indexes, bracket notation is required.

var c = { 9: "a number" };
console.log(c[9]); //outputs "a number"

Properties have attributes that all default to true:
- configurable: can be deleted or changed/edited
- enumerable: property can be returned in a for/in loop
- writable: property can be changed/edited
(ECMAScript also has access modifiers (getters/setters))

All properties on built-in prototypes are non-enumerable, by standard.
When adding your own methods or properties to built-in prototypes, it is suggested to use the "defineProperty" method so you can make the property non-enumerable.

Inherited properties are defined on the object's prototype. Inherited properties can be overridden without affecting the prototype.

var prototypeA = { a: "Apple" };
var objectC = Object.create(prototypeA);

console.log(objectC.a); //Apple

objectC.a = "Animal";
console.log(objectC.a); //Animal
console.log(prototypeA.a); //Apple

prototypeA.a = "Almond";
console.log(objectC.a); //Animal
console.log(prototypeA.a); //Almond

Own properties are defined on just an instance of the object. The own properties of a prototype are the inherited properties of a derived object.

Some operations consider all properties in the prototype chain, some consider only the own properties.

Methods


var z = obj.fullName();

JSON

JSON stands for JavaScript Object Notation. It is the syntax JavaScript expects when defining objects.

Since it is a simple, clear text format, many other programs use JSON as well.

Syntax:
- Data is in name:value pairs
- Data is separated by commas
- Curly braces hold objects
- Square brackets hold arrays

In the strictest format, all labels and values are written as strings.

Ex:

var x = {
    "people": [
        { "name": "john" },
        { "name": "dick" },
        { "name": "harry" }
    ],
    "time": "12:30"
};

Convert Javascript object to JSON string:

var obj = { a: 1 };
var x = JSON.stringify(obj);

Convert JSON string into JavaScript object:

var str = "{ a: 1 }";
var x = JSON.parse(str);

Prototype

Prototypes are like parent classes in C#.

All objects have a "prototype" property. This points to the parent object of this object. Prototypes can point to other prototypes, in what is called the "prototype chain".

Example:

var prototypeA = { a: "Apple" };

var prototypeB = Object.create(prototypeA);
Object.defineProperty(prototypeB, "b", { value: "Banana" });

var objectC = Object.create(prototypeB);
objectC.c = "Citrus";

console.log(objectC.a); //Apple
console.log(objectC.b); //Banana
console.log(objectC.c); //Citrus

console.log(prototypeA.isPrototypeOf(prototypeB)); //true
console.log(prototypeA.isPrototypeOf(objectC));    //true
console.log(prototypeB.isPrototypeOf(objectC));    //true

console.log(prototypeB.isPrototypeOf(prototypeA)); //false
console.log(objectC.isPrototypeOf(prototypeB));    //false
console.log(objectC.isPrototypeOf(prototypeA));    //false
"isPrototypeOf" differs from "instanceof". "prototypeA.isPrototypeOf" will compare "prototypeA" while "instanceof prototypeA" will compare "prototypeA.prototype".

Changes to the prototype immediately affect all derived objects:

prototypeA.a = "Almond";
console.log(objectC.a); //Almond

Operations

List all enumerable own properties:

var keys = Object.keys(myObject);

List all own properties:

var keys = Object.getOwnPropertyNames(myObject);

Example:

var prototypeA = { a: "Apple" };
var objectB = Object.create(prototypeA);
objectB.b = "Banana";
Object.defineProperty(objectB, "c", { value: "Carrot", enumerable: false });

console.log(Object.keys(objectB));                //["b"]
console.log(Object.getOwnPropertyNames(objectB)); //["b", "c"]

Check that an object has a particular own property:

var hasProperty = myObject.hasOwnProperty(propertyName);

List all enumerable own AND inherited properties:

for(var propertyName in myObject)
{
}

Check that an object has a particular inherited property:

var hasProperty = ("propertyName" in myObject);

Remove property (only affects current object, not entire prototype chain):

delete myObject.propertyName;
//or
delete myObject["propertyName"];

Set property:

myObject.propertyName = "value";
//or
myObject["propertyName"] = "value";
//or
Object.defineProperty(myObject, "propertyName", { value: "value", enumerable: false });
//or
Object.defineProperty(myObject, { propertyA: { value: "value", enumerable: false }, propertyB: { value: "otherValue", enumerable: true } });

Destructuring

(see also Array Destructuring)

Always surround the whole statement with the grouping operator ( ) so that the { object } is not interpreted as a code block.
- Grouping operator is not needed if the statement starts with a keyword, such as "var".
- If you do use the grouping operator, make sure the previous statement ends with a semi-colon (;) so that this ( ) is not interpreted as function arguments.

Pull out multiple property values by name:

var a, b, c;
({ b, a, c } = { a: 10, b: 20, x: 30 });
console.log(a); // outputs 10
console.log(b); // outputs 20
console.log(c); // outputs undefined because no property named 'c'

Pull out multiple property values by different names:

var {a: x, b: y} = {a: 42, b: true};
console.log(x); // outputs 42 
console.log(y); // outputs true                
Convert invalid names to valid names:

var { 'fizz-buzz': fizz_buzz } = { 'fizz-buzz': true };
console.log(fizz_buzz); //outputs true

Default values:

var {a = 1, b = 2} = {a: 3};
console.log(a); // outputs 3
console.log(b); // outputs 2

Different names plus default values:

var {a: x = 1, b: y = 2} = {a: 3};
console.log(x); // outputs 3
console.log(y); // outputs 2

Computed property names:

var key = 'x';
var {[key]: name} = {x: 'Bob'};
console.log(name); //outputs Bob

Nested data:

var metadata = {
    title: 'Scratchpad',
    translations: [
        {
            locale: 'de',
            localization_tags: [],
            last_edit: '2014-04-14T08:43:37',
            url: '/de/docs/Tools/Scratchpad',
            title: 'JavaScript-Umgebung'
        },
        {
            locale: 'en',
            localization_tags: [],
            last_edit: '2018-11-14T03:02:59',
            url: 'google.com',
            title: 'Other'
        }
    ]
};
var {title: englishTitle, translations: [{title: localeTitle}, {title: otherTitle}]} = metadata;
console.log(englishTitle); // outputs Scratchpad
console.log(localeTitle);  // outputs JavaScript-Umgebung
console.log(otherTitle);   // outputs Other

Looping through data:

var people = [
  {
    name: 'Mike Smith',
    family: { mother: 'Jane Smith', father: 'Harry Smith', sister: 'Samantha Smith' },
    age: 35
  },
  {
    name: 'Tom Jones',
    family: { mother: 'Norah Jones', father: 'Richard Jones', brother: 'Howard Jones' },
    age: 25
  }
];
for (var {name: n, family: {father: f}} of people) 
{
    console.log('Name: ' + n + ', Father: ' + f);
}
// outputs Name: Mike Smith, Father: Harry Smith
// outputs Name: Tom Jones, Father: Richard Jones

Default values in function parameters:

function myFunc({size = 'big', coords: {x = 0, y = 0}, radius = 99} = {}) {
    console.log(size, x, y, radius);
}
myFunc({ coords: {x: 18}, radius: 22 }); //outputs big 18 0 22 
The final "= {}" is so that the function can be called with zero parameters.

Default values in function parameters can refer to earlier parameters:

function myFunc([a, b] = [1, 2], {x: c} = {x: a + b}) 
{
    return a + b + c;
}
console.log(myFunc()); //outputs 6 because 1 + 2 + (1 + 2)

Spread Syntax

Create clones of objects:

var objA = { a: 0, b: 1, c: { d: 2 } };
var objB = { x: { ...objA } };
console.log(objB); //outputs { x: { a: 0, b: 1, c: { d: 2 } } }
Arrays

Arrays are a special object type.

New


var x = ["a", "b", "c"];
console.log(x[1]); //"b"


var x = new Array("a", "b", "c"); //not recommended
console.log(x[1]); //"b"

var y = new Array(40, 100); //creates an array of two integers
var z = new Array(40); //creates an empty array of length 40

A single array can hold elements of different types.

Access

Indexes start at zero.


var x = ["a", "b", "c"];
x[0] = "A";
console.log(x[0]); //"A"

JavaScript does not support associative arrays aka hashes - indexes are always integers, not strings.

Conversion

If an primitive is expected, arrays are automatically converted to a comma-separated-list.

var x = ["a", "b", "c"];
console.log(x); //"a,b,c"
You can do the same thing explicitly with array.toString()

Operations

length = returns length of array

adding/removing

var x = [];
x.push(element); //add element to end
x.unshift(element); //add element to beginning
x[x.length] = element; //add element to end - not recommended

var y = x.pop() //remove element from end and returns it
var z = x.shift() //remove element from beginning and returns it

toString

var x = ["a", "b", "c"];
var y = x.toString(); //"a,b,c"

join

var x = ["a", "b", "c"];
var y = x.join(" | "); //"a | b | c"

remove or delete

var x = ["a", "b", "c"];
delete x[1]; //changes "b" to undefined

merge or concat

var x = ["a", "b", "c"];
var y = ["d", "e"];
var z = x.concat(y); //["a", "b", "c", "d", "e"]
concat can accept any number of arguments

reverse - edits current array

var x = ["a", "e", "t", "d", "h"];
x.reverse(); //["h", "d", "t", "e", "a"]

slice - copies part of an array into another array
array.slice(startIndex) returns array copied from startIndex to end

var x = ["a", "b", "c", "d", "e", "f"];
var y = x.splice(3); //["d", "e", "f"]

array.slice(startIndex, endIndex) returns array copied from startIndex to endIndex, not including endIndex

var x = ["a", "b", "c", "d", "e", "f"];
var y = x.splice(1, 4); //["b", "c", "d"]

splice - remove x elements starting at startIndex.

array.splice(startIndex, x);

Remove x elements starting at startIndex, and insert any number of new elements starting at startIndex.

array.splice(startIndex, x, newA, newB, newC);

sort
array.sort() defaults to sorting alphabetically, it edits the current array

var x = ["g", "e", "u", "d", "w"];
x.sort(); //["d", "e", "g", "u", "w"]

array.sort(compareFunction) will sort using the function you provide

var x = [45, 3, 21, 6, 9, 11];
x.sort(); //[11, 21, 3, 45, 6, 9]

x.sort(numericCompare); //[3, 6, 9, 11, 21, 45]

function numericCompare(a, b) {
    return a - b;
}
The compare function must return a numeric value:
>0 if a>b
0 if a==b
<0 if a<b

sorting an array randomly

function randomCompare() {
    return 0.5 - Math.random();
}

Is Array

How to verify an object is an array? There are several methods.

This looks the most reliable

if(x instanceof Array) {}

Destructuring

(see also Object Destructuring)

Setting multiple values at once:

var a, b, c;
[a, b, c] = [1, 2, 3];
console.log(a + ", " + b + ", " + c); //outputs 1, 2, 3
Or

var [a, b, c] = [1, 2, 3];
console.log(a + ", " + b + ", " + c); //outputs 1, 2, 3

With rest parameter:

var a, b, c;
[a, b, ...c] = [1, 2, 3, 4, 5, 6];
console.log(a + ", " + b); //outputs 1, 2
console.log(c);            //outputs [3, 4, 5, 6]

Provide defaults in case the unpacked value is undefined:

var [a=5, b=7] = [1];
console.log(a + ", " + b); //outputs 1, 7

Swap values in one statement:

var a = 1, b = 3;
console.log(a + ", " + b); //outputs 1, 3
[a, b] = [b, a];
console.log(a + ", " + b); //outputs 3, 1

Parse a return value:

function f() {
    return [1, 2, 3, 4];
}
var [a, b] = f(); 
console.log(a + ", " + b); //outputs 1, 2

var [c,,,d] = f(); 
console.log(c + ", " + d); //outputs 1, 4

Spread Syntax

Spread syntax allows an iterable collection (such as an array or string) to be automatically divided into an argument list.

Function parameters:

function sum(x, y, z) 
{
    return x + y + z;
}
var numbers = [1, 2, 3];
console.log(sum(...numbers)); //outputs 6

function myFunc(v, w, x, y, z) 
{
    console.log(v, w, x, y, z);
}
var args = [0, 1];
myFunc(-1, ...args, 2, ...[3]); //outputs -1 0 1 2 3

Array literals:

var numbers = [1, 2, 3];
var array = [...numbers, 'A', 'B', 'C'];
console.log(array); //outputs [1, 2, 3, 'A', 'B', 'C']

Clone array: (only goes one level deep)

var arrayA = [1, 2, 3];
var arrayB = [...arrayA];
Date

An object type.

JavaScript string format: Tue Oct 24 2017 20:37:53 GMT-0600 (Mountain Standard Time)
JavaScript integer format: 1508899073536 (milliseconds since 1970/1/1 00:00:00)

New

Current date

var x = Date();
var y = new Date();

New date

var x = new Date(milliseconds);

var y = new Date(dateString);

var a = new Date(year, month, day);
var b = new Date(year, month, day, hours);
var c = new Date(year, month, day, hours, minutes);
var d = new Date(year, month, day, hours, minutes, seconds);
var e = new Date(year, month, day, hours, minutes, seconds, milliseconds);

Date string examples

var a1 = new Date("2017-11-24"); //ISO International Standard - most reliable across browsers - "YYYY-MM-DD"
var a2 = new Date("2017-11-24T12:00:00Z"); //ISO International Standard - with time and zone
var a3 = new Date("2017-11-24T12:00:00-06:30"); //ISO International Standard - with time and UTC relative zone

var b = new Date("11/24/2017"); //short date - "MM/DD/YYYY" - not supported by all browsers

var c1 = new Date("October 24 2017"); //long date - "MMMM DD YYYY"
var c2 = new Date("Oct 24 2017"); //long date - "MMM DD YYYY"
var c3 = new Date("24 Oct 2017"); //long date - "DD MMM YYYY"

var d = new Date("Tuesday October 24 2014"); //full date
var e = new Date("October 24, 2017 11:13:00");

ISO dates can be set with just YYYY-MM or YYYY.

The timezone defaults to the browser's timezone.
"Z" is the timezone code for UTC aka GMT.

Indexes

Months range from 0 to 11.

ToString

date.toString() returns string

date.toUTCString() returns string

date.toDateString() returns string


var date = new Date();

var x = date.toString(); //Tue Oct 24 2017 20:44:45 GMT-0600 (Mountain Standard Time)
var y = date.toUTCString(); //Wed, 25 Oct 2017 02:45:22 GMT
var z = date.toDateString(); //Tue Oct 24 2017

Parse


var x = Date.parse("March 21, 2012"); //milliseconds since 1970/01/01 00:00:00

Methods

getFullYear() //yyyy
getDate() //day 1-31
getDay() //weekday 0-6 with 0 as Sunday
getHours() //hours 0-23
getMinutes() //minutes 0-59
getSeconds() //seconds 0-59
getMilliseconds() //milliseconds 0-999
getTime() //milliseconds since 1970/01/01 00:00:00

Everything except getDay() has a "set" conterpart.

Comparison

Dates can be compared with normal comparison operators.
Math

Constants

Math.PI
Math.E
...others

Random

Math.random() returns a random number in range [0, 1) : includes 0, excludes 1


var x = Math.floor(Math.random() * 100); //random value from 0 to 99

general function to get an integer from min to max, both included

function getRandomInteger(min, max) 
{
    return Math.floor(Math.random() * (max - min + 1) ) + min;
}

Min Max


var x = Math.min(0, 150, -37, 45.7, -19); //-37
var y = Math.max(0, 150, -37, 45.7, -19); //150

getting the min/max value from an array

var x = [5, 8, 2, 3, 8, 4, 6, 2];
var y = Math.min.apply(null, x); //2
var z = Math.max.apply(null, x); //8

Round

Math.round(number) returns integer


var x = Math.round(4.7); //5
var y = Math.round(4.4); //4

Math.ceil(number) rounds up


var x = Math.ceil(4.7); //5
var y = Math.ceil(4.4); //5

Math.floor(number) rounds down


var x = Math.floor(4.7); //4
var y = Math.floor(4.4); //4

Pow

Math.pow(base, exponent) returns number = base ^ exponent


var x = Math.pow(5, 2); //25

Sqrt

Math.sqrt(number) returns number


var x = Math.sqrt(64); //8

Abs

Math.abs(number) returns the absolute value of the number


var x = Math.abs(64); //64
var y = Math.abs(-64); //64

Misc

sin
cos
tan

asin
acos
atan

exp
log

Regular Expressions

Can be used with string methods: match, replace, search, split.
Can be used with RegExp methods: exec, test

RegExp.test(string) returns true if a match is found.
RegExp.exec(string) returns the next match, or null. Must be called repeatedly to get all matches.


var regex = new RegExp("b+", "g");
var matches = getMatches(regex, "abbbcbbc");
function getMatches(regex, text)
{
    var matches = [];
    var match = null;
    while((match = regex.exec(text)) !== null)
    {
        matches.push(match);
    }
    return matches
}

Warning: there is a flaw in the Javascript RegExp exec process. If the RegExp matches an empty string, which is zero-width, it will not increment the cursor index and will enter an infinite loop.
To avoid that:

var regex = new RegExp("b+", "g");
var matches = getMatches(regex, "abbbcbbc");
function getMatches(regex, text)
{
    var matches = [];
    var match = null;
    while((match = regex.exec(text)) !== null)
    {
        matches.push(match);
        if (match.index === regex.lastIndex)
            regex.lastIndex++;
    }
    return matches
}
You can test these on pattern "a*" and text "a aa aaa".

If the global flag (g) is used, RegExp.exec will return each match then null.
If the global flag is not used, RegExp.exec will return the same first match indefinitely.
You can check the 'flag' property of the RegExp object to determine what flags were used.

if(regexp.flags.indexOf('g') == -1)
{
    //only call exec once
}

Instantiation


var regex1 = /b+/g;
var regex2 = new RegExp("b+", "g");

Use the literal for expressions that will not change and will be used many times. It is only compiled once.

Use the constructor for expressions that are only used once or come from user input.

Patterns

Javascript and C# use the same regular expression patterns. See C# notes, Regular Expression Patterns section.

Or see Lessons section, Regular Expressions in javascript.

Exceptions:
    Options:
        g = global search
        i = case insensitive
        m = multiline search
        u = interpret pattern as unicode
        y = sticky = start at current position in string
    Grouping Constructs:
        named groups are not allowed, just ordinals
        in fact, looks like only basic (subexpressions) are allowed
    Substitions
        $_ doesn't work for substituting entire input string

Replace

Simple:

var text = "abcbbcba";
text = text.replace(/c/g, "C"); //text="abCbbCba"

With ordinal groups:

var text = "There is 1 cat and 11 dogs.";
text = text.replace(/(\D)1(\D)/g, "$1one$2"); //text="There is one cat and 11 dogs."

With custom alterations:

//capitalizes only the first alphabet-character
var text = "  this sentence.";
text = text.replace(/(\w)/, w => w.toUpperCase()); //text="  This sentence"
        
Function

Declaration


function MyFunction() {
}

function Area(width, height) {
    return width * height;
}

Parameters: the variable names listed in the declaration.
Arguments: the values passed into the function.
Returns: undefined by default

Invocation

var area = Area(5, 10);

Function as variable

var myFunction = Area;
console.log(myFunction.name); //outputs Area

Functions can be declared within "if" statements, but this is implemented differently in different Javascript engines so it is not recommended. Use function expressions instead if you need this design pattern.

Anonymous Expression

You can create anonymous functions as expressions.

var multiply = function(a, b) {
    return a * b;
}
console.log(multiply(3, 4)); //outputs 12
//The function name defaults to the name of the variable.
console.log(multiply.name); //outputs multiply

An assigned function expression may call itself because the assigned variable is within scope.

var fibonacci = function (num) {
    if(num <= 0) return 0;
    if(num <= 2) return 1;
    return fibonacci(num - 1) + fibonacci(num - 2);
};
console.log(fibonacci(6)); //outputs 8, which is the 6th fibonacci number

Function expressions are frequently used for callbacks.

function run(callback)
{
    console.log(callback.name);  //outputs empty string
    return callback();
}
var x = run(function(){ return 10; });
console.log(x); //outputs 10

Function expressions are frequently used in objects.

var math = {
    'myFunc': function () {
        console.log('a');
    }
};
math.myFunc(); //outputs a

Named Expression

You may specify a name for a function expression. This is recommended to improve legibility of the code, of error messages, and of stack traces.

function run(callback)
{
    console.log(callback.name);  //outputs display10
    return callback();
}
var x = run(function display10(){ return 10; });
console.log(x); //outputs 10

When the function name is specified, the function may call itself recursively. The function cannot be called by name outside of its own scope.

function run(callback)
{
    return callback(6);
}

var result = run(function fibonacci(num) {
    if(num <= 0) return 0;
    if(num <= 2) return 1;
    return fibonacci(num - 1) + fibonacci(num - 2);
});

console.log(result); //outputs 8

Constructor

You can declare and define a function dynamically with the function constructor. This is not recommended as is has security risks and performance issues.

These functions are defined the same way:

var constructedSum = new Function('a', 'b', 'return a + b');

function declaredSum(a, b)
{
    return a + b;
}

console.log(constructedSum(2, 6)); // outputs 8            
console.log(declaredSum(2, 6));    // outputs 8            

The arguments passed to "new Function" are any number of parameter names, ending with the function body.
The parameter names may be specified as one comma-delimited string.
Everything is parsed when the function object is created.

The "new" keyword is optional for some reason.

var constructedSum = Function('a', 'b', 'return a + b');
console.log(constructedSum(2, 6)); // outputs 8            

Constructed functions are always in the global scope, no matter where they where created from. They do not create closures to their creation contexts.

var x = 10;
function useConstructor() {
    var x = 20;
    return new Function('return x;');
}
function useConstructorImmediately() {
    var x = 30;
    return (new Function('return x;'))();
}
function useExpression() {
    var x = 40;
    return function() {
        return x;
    }
}
var a = useConstructor();
var b = useConstructorImmediately();
var c = useExpression();
console.log(a()); //outputs 10
console.log(b);   //outputs 10
console.log(c()); //outputs 40

One use of the function constructor is to access the global object from a bound scope:

(function() {
    'use strict';
    var global = new Function('return this')();
    console.log(global === window); //outputs true
    console.log(this === window);   //outputs false
})();

Arrow Expression

Arrow function expressions are notably different from other ways of specifying a function:
- They do not create a local "this". They access the "this" from the surrounding scope.
- They do not have a local "arguments" object.
- They do not have a "super" object.
- They cannot be used as constructors (with the "new" keyword).
- They do not have a "prototype" property.
- They cannot be used as generators.

Why use an arrow function?
- the same functionality can be written shorter
- since they do not create their own "this" value, they can be used to simplify specific use cases

Syntax for parameters:

var a = (param1,..,paramN) => { statements };

//these are the same
var b1 = (param1) => { statements };
var b2 = param1 => { statements };

var c = () => { statements };
Default parameters and the rest parameter are supported.

Syntax for function body:

var a  = (param1,..,paramN) => { statements };

//these are the same
var b1 = (param1,..,paramN) => one_expression; //"concise body" has an implied return
var b2 = (param1,..,paramN) => { return one_expression }; //"block body" has an explicit return

To return an object literal, wrap it in a grouping operator:

var a = (param1,..,paramN) => ({name: value});
You cannot put line breaks between the parameters and the arrow.

Writing shorter code:

var elements = [ 'Hydrogen', 'Helium', 'Lithium', 'Beryllium' ];
var a = elements.map(function(element) { return element.length; });
var b = elements.map(element => { return element.length; });
var c = elements.map(element => element.length);
var d = elements.map(({ length }) => length); //I'm not sure how this one works; it may be related to destructuring

The old way of accessing an outer "this" value:

function Person() {
    var that = this;
    that.age = 0;

    setInterval(function growUp() {
        that.age++;
    }, 1000);
}
var person = new Person();
The new way, using arrow functions:

function Person(){
    this.age = 0;

    setInterval(() => {
        this.age++;
    }, 1000);
}
var person = new Person();
When to NOT try to use this:

var chopper = {
    owner: 'Zed',
    getOwner: () => this.owner //won't work because "this" is referring to undefined or the "window" or the global object, depending on the engine
};
console.log(chopper.getOwner());
Even with "use strict" turned on, arrow functions will not create their own "this" value.

Arrow functions don't have their own "arguments" object. They use the one from the surrounding scope.

function plusOne(n) {
    var f = () => arguments[0] + n;
    return f(1);
}
console.log(plusOne(3)); // outputs 6 because n + n
Use a rest parameter instead:

function plusOne(n) {
    var f = (...rest) => rest[0] + n;
    return f(1);
}
console.log(plusOne(3)); // outputs 4

If you get parsing errors, wrap the arrow function in the grouping operators:

let callback;
callback = callback || function() {}; // ok
callback = callback || () => {};      // SyntaxError: invalid arrow-function arguments
callback = callback || (() => {});    // ok

Examples of simple lambdas written with arrow function expressions:

var nums = [5, 6, 13, 0, 1, 18, 23];
var sum = nums.reduce((a, b) => a + b);      // 66 which is the sum of all the elements
var even = nums.filter(v => v % 2 == 0);     // [6, 0, 18] which are all the even values
var double = nums.map(v => v * 2);           // [10, 12, 26, 0, 2, 36, 46] which is the double of each value
var anyZero = nums.some(item => item === 0); // true because at least 1 element equaled 0

If you want to use an arrow function for an event handler, you'll need to pass the "event" object as an argument since "this" won't be available.

button.addEventListener('click', function () {
    this.classList.toggle('on');
});
//versus
button.addEventListener('click', (event) => {
    event.currentTarget.classList.toggle('on');
});

Shorthand Method

Benefits:
- shorter
- more legible
- requires a function name which is a good coding practice


var collection = {  
    items: [],
    add: function(...items) {
        this.items.push(...items);
    },
    get: function(index) {
        return this.items[index];
    }
};
collection.add('C', 'Java', 'PHP');
console.log(collection.get(1)); //outputs Java
is the same as

var collection = {  
    items: [],
    add(...items) {
        this.items.push(...items);
    },
    get(index) {
        return this.items[index];
    }
};
collection.add('C', 'Java', 'PHP');
console.log(collection.get(1)); //outputs Java

Classes always use shorthand method definitions:

class Star {  
    constructor(name) {
        this.name = name;
    }
    getName() {
        return this.name;
    }
}
var sun = new Star('Sun');  
console.log(sun.getName()) //outputs Sun

Computer property names with shorthand method definitions:

var addMethod = 'add';
var getMethod = 'get';
var collection = {  
    items: [],
    [addMethod](...items) {
        this.items.push(...items);
    },
    [getMethod](index) {
        return this.items[index];
    }
};
collection[addMethod]('C', 'Java', 'PHP');  
console.log(collection[getMethod](1)); //outputs Java

Generator

A generator function returns an iterator object. The function will not be executed all at once; its state will be saved and execution will step forward to the next "yield" expression each time you call "next()" on the iterator.

Generators end on:
- end of function
- a return statement, in which case the returned value is the last "yielded" value
- an uncaught exception


function* generator(i) {
    yield i++;
    yield i++;
    yield i++;
}
var iterator = generator(0);
console.log(iterator.next().value); //outputs 0
console.log(iterator.next().value); //outputs 1
console.log(iterator.next().value); //outputs 2
console.log(iterator.next().value); //outputs undefined

You can loop through all the yielded values of an iterator:

function* generator() {
    yield 'a';
    yield 'b';
    yield 'c';
}
var str = "";
for (let val of generator()) {
    str = str + val;
}
console.log(str); // outputs abc

A generator can be an anonymous expression:

var x = function*(y) {
    yield y;
};

Iterator.next() returns an object like { 'value': yielded_value, 'done': false }.
When the last "yield" expression is reached, "done" is returned as false. All future calls to "next()" result in { 'value': undefined, 'done': true }.

If there are any statements after the last "yield" expression, they will be run if you call "next()" one more time. After that, there is nothing left in the generator function to run.

function* generator(i) {
    yield i++;
    console.log("more code");
}
var iterator = generator(0);
console.log(iterator.next().value); //outputs 0
console.log(iterator.next().value); //outputs "more code" and undefined
console.log(iterator.next().value); //outputs undefined

You can pass an argument in "next()", but I'm not clear on what that does.
But here is an interesting example:

function* generator()
{
    var reply = yield 'What is the letter?';
    console.log(reply);
    reply = yield 'What is the letter after that?'
    console.log(reply);
}
var iterator = generator();
console.log(iterator.next().value);
console.log(iterator.next('A').value);
iterator.next('B');
//outputs What is the letter?
//outputs A
//outputs What is the letter after that?
//outputs B
You cannot pass an argument to the FIRST call to "next()" on an iterator.

Yield* can delegate execution to another generator:

function* anotherGenerator(i) {
    yield i + 1;
    yield i + 2;
    yield i + 3;
}
function* generator(i) {
    yield i;
    yield* anotherGenerator(i);
    yield i + 10;
}
var gen = generator(0);
console.log(gen.next().value); // outputs 0
console.log(gen.next().value); // outputs 1
console.log(gen.next().value); // outputs 2
console.log(gen.next().value); // outputs 3
console.log(gen.next().value); // outputs 10                
Or over any iterable, such as an array of values:

function* generator(i) {
    yield i;
    yield* [-1, -2, -3];
    yield i + 10;
}
var gen = generator(0);
console.log(gen.next().value); // outputs 0
console.log(gen.next().value); // outputs -1
console.log(gen.next().value); // outputs -2
console.log(gen.next().value); // outputs -3
console.log(gen.next().value); // outputs 10                

Yield can only be used within the scope of a generator, so this is not valid:

function* generator() {
    [1, 2].forEach(function (item) {
        yield item; // SyntaxError: yield expression is only valid in generators
    });
}
var iterator = generator();
console.log(iterator.next());

Example use case: get unlimited random values from a set of options:

function * randomFrom(...arr) {
    while (true)
    {
        yield arr[Math.floor(Math.random() * arr.length)];
    }
}
const getRandom = randomFrom(1, 2, 5, 9, 4);
console.log(getRandom.next().value); // returns random value

You can create a generator function dynamically, but it is not recommended due to security and performance issues.
It works like the Function Constructor:

var GeneratorFunction = Object.getPrototypeOf(function*(){}).constructor
var g = new GeneratorFunction('a', 'yield a * 2');
var iterator = g(10);
console.log(iterator.next().value); // 20                

Hoisting

Function declarations (with definition) are hoisted to the top of the current scope, which means either (A) the top of the enclosing function or (B) the global scope. Therefore you can call a function before (earlier in the file) it is declared.

myFunc(); //outputs 1

function myFunc()
{
    console.log(1);
}

If more than one function has the same name (regardless of parameters), the last function definition will overwrite the earlier ones.

It is standard practice to put all function declarations/definitions at the end of your file, and keep the high-level statements at the beginning.

Only function declarations are hoisted. Function expressions and arrow functions are not hoisted.

Scope

Code blocks create a local scope. Functions are a code block, so they have a local scope that includes their arguments.

Functions can still make use of any global variables, provided their names are different from the local variables.

If you assign a value to an undeclared variable, its scope will automatically be global. Don't do this.

Generally avoid global variables, especially in web development. You may be overwriting another script's variable or function, and they could overwrite yours.

Parameters

You can pass any number of parameters to any function.
If there are extra parameters, they will be ignored.
If there are not enough parameters, the extras will be set to undefined.

A full list of the parameters passed to a function is accessible through the "arguments" object.
Even if there are not enough parameters to capture all the passed arguments, they will all be in the "arguments" object.

function myFunc(a, b, c)
{
    console.log(arguments[0]); //outputs value of a
    console.log(arguments[1]); //outputs value of b
    console.log(arguments[2]); //outputs value of c
}

Default parameters provide a default value for a parameter in case no value is passed in:

function add(a, b=2) {
    return a + b;
}
console.log(add(1)); //outputs 3
console.log(add(1, 1)); //outputs 2
console.log(add(1, null)); //outputs 1

A function may have one rest parameter, as the last parameter.
A rest parameter will capture all excess arguments in one array.

function myFunc(a, b, ...rest)
{
    console.log(rest);
}
myFunc(1, 2, 3, 4, 5); //outputs [3, 4, 5]
A rest parameter can have any name.

Arity

The arity of a function is the number of arguments it expects.


//arity = 2
//sum.length = 2
function sum(a, b)
{
    return a + b;
}

//arity = 2
//sum.length = 0
function sum()
{
    return arguments[0] + arguments[0];
}

Closure

A closure is a function defined inside another function. A closure can only be called by its outer-function.
Update: it may be more correct to say a closure is a any function plus its enclosing scope, since top level functions do have access to the global scope

The inner-function's scope includes itself, the outer-function, and global. I.e. the closure is a combination of the inner-function and the lexical environment it was declared in.


function A(one, two)
{
    B();
    function B()
    {
        console.log(one);
        console.log(two);
    }
}
A(1, 2); //prints 1 2

Closures have access to the outer-function's scope even after the outer-function has returned.

function A(one, two)
{
    var three = 3;
    return function() { 
        return one + two + three;
    };
}
var addition = A(1, 2);
console.log(addition()); //prints 6

That includes other inner-functions of the outer-function:

function A()
{
    function B() { console.log('B'); }
    return function() { B(); };
}
A()(); //prints 'B'

Closures do this by storing references to the outer-function's variables. Therefore, if the value of those variables change before the closure is run, the closure will be working with the most up-to-date values.

function property()
{
    var property = 10;
    return {
        getProperty: function() { return property; },
        setProperty: function(value) { property = value; }
    };
}
var propertyObject = property();
propertyObject.setProperty(12);
console.log(propertyObject.getProperty()); //prints 12
The "getProperty" and "setProperty" here are called "privileged methods" because they are the only way available to access the internal "property".

Closures have access to the scope of their outer-function. If the outer-function is also a closure, then the scopes add up. Essentially, the inner-most closure has access to the scope of all wrapping functions up to the outer-most wrapping function.

function sum(a){
    var e = 1;
    return function(b){
        return function(c){
            return function(d){
                return a + b + c + d + e;
            }
        }
    }
}
console.log(sum(1)(2)(3)(4)); //prints 11

Immediately Invoked Function Expression

Also known as Self Executing Anonymous Function.
A function that runs as soon as it is defined, and only runs once.


(function() {
    console.log('a');
})();
The parentheses around the function expression are the grouping operator. They create a new scope for the function: global variables can be accessed within the function but variables declared within the function do not pollute the global space.
The last pair of parentheses cause the function to execute immediately.

You can store the function result, but not the function itself.

var result = (function() {
    return 5;
})();
console.log(result); //outputs 5

Incorrectly written closure:

function init(array)
{
    for(var i = 0; i < array.length; i++)
    {
        array[i] = function() { return i; }
    }
    return array;
}
var numbers = init(new Array(5));
console.log(numbers[0]()); //prints 5
console.log(numbers[1]()); //prints 5
console.log(numbers[2]()); //prints 5

Correctly written closure using "immediately invoked function expression":

function init(array)
{
    for(var i = 0; i < array.length; i++)
    {
        array[i] = function() { return i; }()
    }
    return array;
}
var numbers = init(new Array(5));
console.log(numbers[0]); //prints 0
console.log(numbers[1]); //prints 1
console.log(numbers[2]); //prints 2
Just put a "()" at the end of the function to cause it to execute immediately.

Alternative implementation, passing variables into the immediately invoked function:

function init(array)
{
    for(var i = 0; i < array.length; i++)
    {
        array[i] = function(number) { return number; }(i)
    }
    return array;
}
var numbers = init(new Array(5));
console.log(numbers[0]); //prints 0
console.log(numbers[1]); //prints 1
console.log(numbers[2]); //prints 2

Alternative implementation, using "let" instead:

function init(array)
{
    for(var i = 0; i < array.length; i++)
    {
        let j = i;
        array[i] = function() { return j; }
    }
    return array;
}
var numbers = init(new Array(5));
console.log(numbers[0]()); //prints 0
console.log(numbers[1]()); //prints 1
console.log(numbers[2]()); //prints 2
"let" defines a block-scoped variable. The scope of the variable is limited to the block/statement/expression on which it is used.

"Global import" is the concept of passing global variables into an Immediately Invoked Function Expression, so that it is clear what global variables you intend to operate on.

Partial Application

Application is the process of applying a function to its arguments to produce a return value.

In partial application, the function and some of its arguments are passed to a second function. The local scope of the second function acts as storage for these values. The second function can be called repeatedly, filling in the missing arguments of the first function with different values each time.


function partialOperation(fullOperation, a, b, c)
{
    return function(d, e) {
        return fullOperation(a, b, c, d, e);
    };
}

function sum(a, b, c, d, e)
{
    return a + b + c + d + e;
}

function multiply(a, b, c, d, e)
{
    return a * b * c * d * e;
}

var partiallyApplied = partialOperation(sum, 1, 2, 3);
var finalAnswer = partiallyApplied(4, 5); //15

partiallyApplied = partialOperation(multiply, 1, 2, 3);
finalAnswer = partiallyApplied(4, 5); //120

General purpose partial applicator for any number of arguments. This makes use of the build-it "arguments" array that holds all arguments passed into a function.

function partialApplicator(fn)
{
    var slice = Array.prototype.slice;
    var args = slice.call(arguments, 1); //get all arguments after fn
    
    return function()
    {
        //concat previous arguments with new arguments and apply all to fn
        return fn.apply(this, args.concat(slice.call(arguments, 0)));
    }
}

function sum(a, b)
{
    return a + b;
}

function multiply(a, b, c, d)
{
    return a * b * c * d;
}

var partiallyApplied = partialApplicator(sum, 1);
var finalAnswer = partiallyApplied(2);
console.log(finalAnswer);

partiallyApplied = partialApplicator(multiply, 1, 2);
finalAnswer = partiallyApplied(3, 4);
console.log(finalAnswer);

Currying

Currying is transforming a function with N arguments into a chain of functions, each with 1 argument. You end up calling a series of functions, gradually filling in the arguments needed by the original function. This is a different technique than partial application.

Currying is a functional programming concept that is built into other languages, like Haskell or Elm, but is only simulated in Javascript. (In Haskell and Elm, all functions are automatically compiled as a series of curried functions.)

Curried functions will always have this nested, one parameter at a time, structure:

function ABC(a)
{
    return function(b)
    {
        return function(c)
        {
            return a + b + c;
        }
    }
}
    
var result = ABC(1)(2)(3);
console.log(result); //outputs 6

var bc = ABC(1);
var c = bc(2);
result = c(3);
console.log(result); //outputs 6

Curried function structure using arrow functions:

var DEF = d => e => f => d + e + f;
var result = DEF(1)(2)(3);
console.log(result); //outputs 6

"Overloaded Currying": Curried function structure sort of like method overloading:

function sum(...args) {
    if (args.length < 2) {
        return sum.bind(this, ...args);
    }
    return args[0] + args[1];
}
console.log(sum(1,2)); //outputs 3
console.log(sum(1)(2)); //outputs 3

Real example:

const tag = t => contents => `<${t}>${contents}</${t}>`;
const boldTag = tag('b');
console.log(boldTag('Apple')); //outputs "<b>Apple</b>"
console.log(boldTag('Banana')); //outputs "<b>Banana</b>"

To automatically turn any function into a curried function:

function curry(fn, numberOfArguments)
{
    if(typeof numberOfArguments !== 'number')
    {
        //function.length is the number of expected arguments
        numberOfArguments = fn.length;
    }
        
    function getCurriedFunction(previous)
    {
        return function(arg)
        {
            //add one argument to array on each invocation
            var args = previous.concat(arg);
            if(args.length < numberOfArguments)
                return getCurriedFunction(args);
            //once you have enough arguments, run the function
            else
                return fn.apply(this, args);
        };
    }
    
    //init with empty array of arguments
    return getCurriedFunction([]);
}

function sum(a, b, c)
{
    return a + b + c;
}

var curriedSum = curry(sum);
var result = curriedSum(1)(2)(3);
console.log(result);

Considerations when using currying:
- currying is not compatible with default parameter values
- curried functions are most useful when the first parameters are settings and the last are the data being operated on, which is backwards of the usual order of parameters

Bind

With bind, you can set the "this" value for a function, plus the first parameters of the function. A new function is returned. When you invoke the new function, you can provide any remaining parameters.

Syntax:

var newFunction = oldFunction.bind(thisValue, a, b, c);
var result = newFunction(d, e);

Example: setting "this"

function getAttribute(attributeName)
{
    return this[attributeName];
}    
var customer = { id: 12, name: "Bob", age: "44" };
var getBobAttribute = getAttribute.bind(customer);
console.log(getBobAttribute("name")); //outputs Bob
console.log(getBobAttribute("age")); //outputs 44

Compose, Pipe

A function is composable if it has one parameter and one return value.

Function composition allows you to combine two or more functions into one new function. Function composition is the same concept as method chaining in C# and as piping in Unix command prompt.

"compose" is not a built-in function:

const compose = (...functions) => args => functions.reduceRight((inputArgument, currentFunction) => currentFunction(inputArgument), args);

"pipe" is not a built-in function:

const pipe = (...functions) => args => functions.reduce((inputArgument, currentFunction) => currentFunction(inputArgument), args);

The only difference between compose and pipe is the order they execute functions in. Pipe executes left-to-right (like a Unix command line pipe). Compose executes right-to-left (like a series of nested function calls).


function reverse(text)
{
    return text.split('').reverse().join('');
}
function drop(text)
{
    return text.substring(0, text.length - 1);
}
function repeat(text)
{
    return text + text;
}
console.log(reverse("bird")); //outputs "drib"
console.log(drop("bird")); //outputs "bir"
console.log(repeat("bird")); //outputs "birdbird"

const compose = (...functions) => args => functions.reduceRight((inputArgument, currentFunction) => currentFunction(inputArgument), args);
const pipe = (...functions) => args => functions.reduce((inputArgument, currentFunction) => currentFunction(inputArgument), args);

var dropReverseRepeat = pipe(drop, reverse, repeat);
console.log(dropReverseRepeat("bird")); // outputs "ribrib"

var repeatReverseDrop = compose(drop, reverse, repeat);
console.log(repeatReverseDrop("bird")); // outputs "dribdri"

Thunk

A function that wraps a call to another function, with arguments, for later use.

High Order

A high order function is a function that returns a function.

Pure Function

A pure function is a function with no side effects. It accepts arguments and returns a value, and makes no changes to anything - no database operations, no changes to global values, nothing.

Javascript does not require that functions be pure functions. However, making as many of your functions as possible into pure functions will result in cleaner code (easier to test, better organized).

Trampolining

Trampolining is using a loop that invokes thunk returning functions.

For when you have to run a long recursive operation that would otherwise overflow stack memory.

This is much much slower than recursion.


function trampoline(fn)
{
    return function() {
        var x = fn.apply(this, arguments);
        while(x instanceof Function) {
            x = x();
        }
        return x;
    };
}

function range(start, end, result)
{
    result = result || [];
    result.push(start);
    return (start == end) ? result : function() {
        return range(((start < end) ? ++start : --start), end, result);
    };
}

trampoline(range)(1, 4); //returns [1, 2, 3, 4]
//should this be trampoline(range(1,4)) ?

Monad

Monads are a type of functor that simplify mappings from one type to another type. They are used when one type needs to wrapped (lifted) or unwrapped (flattened) before it can be mapped to the other type.

It is easy to line up these functions into a pipeline, because the input/output line up perfectly.

const AtoB = a => b;
const BtoC = b => c;

It is easy to line up these functions into a pipeline, using functions like "map" which operate of functors.

const AtoB = functor(a) => functor(b);
const BtoC = functor(b) => functor(c);

In order to line up these functions into a pipeline, you need to use monads.

const AtoB = a => monad(b);
const BtoC = b => monad(c);
By using monads, "a => monad(c)" can be composed without adding special code to flatten "monad(b)" into "b". It will be handled automatically by the monad.

Example

Example: this won't work because it is not using monads.

//setup
const compose = (...fns) => x => fns.reduceRight((y, f) => f(y), x);
const trace = label => value => {
    console.log(`${ label }: ${ value }`);
    return value;
};
const label = 'API call composition';

// a => Promise(b)
const getUserById = id => id === 3 ?
    Promise.resolve({ name: 'Kurt', role: 'Author' }) :
    undefined
;

// b => Promise(c)
const hasPermission = ({ role }) => (
    Promise.resolve(role === 'Author')
);

// a => Promise(c) - this won't work automatically
const authUser = compose(hasPermission, getUserById);

authUser(3).then(trace(label)); //outputs API call composition: false

Example: fixed with monads.

//setup
const compose = (...fns) => x => fns.reduceRight((y, f) => f(y), x);
const trace = label => value => {
    console.log(`${ label }: ${ value }`);
    return value;
};
const label = 'API call composition';

//monad setup
const composeM = chainMethod => (...ms) => (
    ms.reduce((f, g) => x => g(x)[chainMethod](f))
);
const composePromises = composeM('then');

// a => Promise(b)
const getUserById = id => id === 3 ?
    Promise.resolve({ name: 'Kurt', role: 'Author' }) :
    undefined
;

// b => Promise(c)
const hasPermission = ({ role }) => (
    Promise.resolve(role === 'Author')
);

// a => Promise(c)
const authUser = composePromises(hasPermission, getUserById);

authUser(3).then(trace(label)); //outputs API call composition: true

Terminology

Wrapping aka lifting aka unit aka of: the monad must provide a function to wrap a value in the monad.

Unwrapping aka flattening aka join aka chain: the monad must provide a function to unwrap a value from the monad.

Map: since monads are functors, they must support mapping Monad(a) to Monad(b).

Kleisli Composition aka chain: chain means to flatten then map. Given Monad(Monad(a)), Monad(b) is returned.
("chain" seems to sometimes mean the same as "join", and sometimes mean "join then map")

Uses

Possible uses of monads:

You want to insert debugging or logging messages into a pipeline. You don't want to edit any of your existing functions.
You could create a pipeline function that outputs the messages before or after each function call.
(actually I'm not sure this counts as a monad, but it a good use of pipe/compose)

The "Maybe" monad is like "Nullable" in C#. It wraps a value or undefined, and lets you operate on either without getting "undefined" errors.

The "List" monad represents a lazy-load list of values.
One implementation is to accept a starting value in the constructor, and a transform function. The starting value is returned first. Each time you ask for a new value, the transformation is applied to the previous value and the result is returned.
(this implementation sounds like a generator function could serve the same purpose)

The "promise" object (in ES6) is an implementation of the "Continuation" monad.
It allows you to line up asynchronous tasks, and the next one will be run when then previous one completes.

The "Proxy" monad wraps a value and includes some special behavior that you want available in addition to the normal behavior of the value. For any function/property that the monad does not recognize, it passes the call to the wrapped value and returns the result.
Logic

All the loops can use 'break' to exit the loop early, and 'continue' to skip to the next iteration.

If Else


if(x < y) {
    z = 5;
}


if(x < y) {
    z = 5;
}
else {
    z = 7;
}


if(x < y) {
    z = 5;
}
else if(x == y) {
    z = 6;
}
else {
    z = 7;
}

For Loop


var x = ["a", "b", "c"];
for(var i = 0; i < x.length; i++)
{
    console.log(x[i]);
}

For In Loop

For..In will iterate over enumerable properties of an object. Order is not consistent.
For..In will iterate over the indexes of an array. But don't use it, because order is not consistent.


var x = { firstName: "John", lastName: "Smith", age: 34 };
var text = "";
for(field in x) {
    text += field + ": " + x[field] + ", ";
}
console.log(text); //"firstName: John, lastName: Smith, age: 34, "

For Of Loop

For..Of iterates over "iterable collections". That's anything with a Symbol.iterator property.
It keeps calling Iterator.next() until 'done' is true.

This does not work on objects.
It works well on arrays, strings, and NodeLists.

array:

var array = ['a','b','c', 'd'];
for(let element of array)
{
    console.log(element);
}

NodeList:

var elements = document.querySelectorAll('.myClass');
for (let element of elements) 
{
    element.addEventListener('click', doSomething);
}

While Loop


var x = 1;
while(x < 100) {
    console.log(x);
    x++;
}


var x = 1;
do {
    console.log(x);
    x++;
} while(x < 100);

Switch


switch(x) {
    case 1: y = 7; break;
    case 2: z = 62; break;
    case 3:
    case 4: z = -5; break;
    default: y = z - 56; break;
}
Events

JavaScript functions can be triggered by a browser event.

HTML Events

Add events with HTML attributes. Any javascript can be entered into the event attribute, including multiple statements.


<html>
    <element event='myFunc()'>
    </element>
    <element event='console.log("1"); console.log("2");'>
    </element>
</html>


<button onclick="document.getElementById('demo').innerHTML = Date()">Time?</button>

Use the 'this' keyword to refer to the current element.

<button onclick="this.innerHTML='Clicked'">Click?</button>

Use the browser-provided 'event' variable to pass the event object to your event handler.

<button onclick="myEventHandler(event)">Click?</button>

Set an event listener programatically.

document.getElementById("myId").addEventListener("click", myEventHandler);

function myEventHandler(event) {
    console.log(event);
    console.log(this); //'this' is set to the element
}
You can add multiple event handlers to the same event.
Note that this method expects events like "click" instead of "onclick".

Remove an event listener programatically.

document.getElementById("myId").removeEventListener("click", myEventHandler);

Event Propagation

Given nested elements that each have a listener for event X, which order should the event handlers be called in?

Bubbling: the inner-most element will run first, working outward. This is the default.

Capturing: the outer-most element will run first, working inward.


document.getElementById("myId").addEventListener("click", myEventHandler, useCapture:true);

Event List

Events not listed elsewhere:
    onload: when a document finishes loading

    onchange: input value changed - for INPUT, SELECT, and TEXTAREA tags
    onfocusin: element is about to gain focus
    onfocus: element gains focus
    onfocusout: element is about to lose focus
    onblur: element loses focus
    oninput: element gets user input
    oninvalid: element is invalid
    onreset: form is reset
    onsearch: user writes something in a search field
    onselect: after a user selects text - for INPUT and TEXTAREA tags
    onsubmit: form is submitted
    ontoggle: user opens or closes a DETAILS element
    
    oncopy: user copies element contents
    oncut: user cuts element contents
    onpaste: user pastes content into an element

    ontouchstart: finger is placed on touch screen
    ontouchmove: finger is dragged across touch screen
    ontouchend: finger is removed from touch screen
    ontouchcancel: touch is interrupted
    
    onbeforeprint: page is about to be printed
    onafterprint: page has started printing, or print dialog is closed
    
    oncanplay: media has buffered enough to begin playing
    oncanplaythrough: media has buffered all the way to the end
    ondurationchange: duration of the media is changed
    onratechange: playing speed of the media is changed
    onvolumechange: volume of the media is changed
    onended: media file has played to the end
    onloadstart: browser begins trying to load media
    onloadeddata: media data is loaded
    onloadedmetadata: media metadata is loaded
    onprogress: browser is currently downloading the media
    onstalled: media data is not available
    onemptied: media file is not unavailable, or network issue
    onsuspend: browser is intentionally not loading media data
    onpause: the media is paused and expects to resume (like for buffering)
    onplay: media is played
    onplaying: media is currently playing
    onpause: media is paused
    onseeking: user is moving to a new position in the media
    onseeked: user finishes moving to a new position in the media
    ontimeupdate: media playing position has changed
    
    animationstart: css animation has started
    animationiteration: css animation is repeated
    animationend: css animation has completed
    transitionend: css transition ends
    
    onabort: loading a resource was aborted
    onbeforeunload: the document is about to be unloaded
    onerror: an error occurred loading a file
    onhashchange: the anchor section of the URL changed
    onpageshow: user navigates to the page
    onpagehide: user leaves the page
    onresize: document view is resized
    onscroll: element's scrollbar is scrolled
    onunload: after a page has been unloaded - for BODY tag only
    onmessage: a message is received from the server
    onopen: a connection with the server is opened
    ononline: browser starts to work online
    onoffline: browser starts to work offline
    onpopstate: window history changes
    onstorage: a web storage area is updated
    
Methods:
    preventDefault(): cancels the event if it is cancelable
        (verified for onkeydown and onkeypress, to block a character being typed)
    stopImmediatePropagation(): prevents other listeners of the same event from being called
    stopPropagaion(): prevents further propagation of event
    
Mouse Events

onclick: left-mouse-click on element
ondblclick: double left-mouse-click on element
oncontextmenu: right-mouse-click on element
onshow: a MENU element is shown as a context (right-click) menu
onmousedown: a mouse button is pressed on an element
onmouseup: a mouse button is released on an element
onwheel: mouse wheel is moved
onmouseenter: mouse moves over element
onmouseleave: mouse moves off element
onmousemove: mouse moves while over an element

onmouseover: mouse moves over element, or one of its children
onmouseout: mouse moves off element, or one of its children

ondragstart: user starts dragging an element
ondrag: element is being dragged
ondragenter: element enters a drop target
ondragover: element is over a drop target
ondragleave: element leaves a drop target
ondrop: element is dropped on a drop target
ondragend: user stops dragging element (mouse button released)

Event Properties:
    button = which mouse button was pressed
        0: left (or main)
        1: middle or wheel
        2: right (or secondary)
        3: browser back (or fourth)
        4: browser forward (or fifth)
    buttons = bitwise flag for which mouse buttons were pressed
        0: none
        1: left (primary)
        2: right (secondary)
        4: middle (auxiliary)
        8: browser back (fourth)
        16: browser forward (fifth)
    detail = number of times the mouse was clicked
    pageX, pageY = x and y coordinates of mouse relative to document
    clientX, clientY = x and y coordinates of mouse relative to window
    screenX, screenY = x and y coordinates of mouse relative to screen
    relatedTarget = ? the element related to the event-triggering-element
    altKey, ctrlKey, shiftKey, metaKey = was this key pressed when the event triggered?
    which => button
    
Keyboard Events
    
onkeydown:
    keyboard key pressed down
    will repeat if key is held down
    occurs before char is added to the text field
onkeypress:
    keyboard key pressed
    will only trigger for displayable character keys (will trigger for "T" but not for "Shift" or "Control")
    (verified in FireFox that is does occur for Backspace, Delete, and the arrow keys)
    will repeat if key is held down
    occurs before char is added to text field
onkeyup:
    keyboard key released
    can only occurs once per key press
    occurs after char is added to text field

Event Properties:
    key = (character) the character of the key
    keyCode = (integer) Unicode character code of the key
    charCode = (integer) Unicode character code of the key (KEYPRESS event only)
    altKey, ctrlKey, shiftKey, metaKey = was this key pressed when the event triggered?
    location = location of key on the keyboard
    which => keyCode
    
    
Error Handling


try {
    myFunc(70);
} 
catch(err) {
    console.log(err.message);
}
finally {
    console.log("always run finally");
}

function myFunc(num) {
    if(num > 50)
        throw { message: "Number must be <= 50." };
    console.log(num);
}

You can throw a string, number, boolean, or object. The example above is throwing an object.

JS Error

When a JavaScript error occurs, it throws the object { name: "", message: "" }

Name can be:
EvalError - eval() threw an error (newer versions of JavaScript throw SyntaxError instead)
RangeError - a number was out of range
ReferenceError - illegal reference
SyntaxError
TypeError
URIError - encodeURI() threw an error

Debugging

console.log(text)

use debugger window to set breakpoints

set a breakpoint in the code that will open the debugger window

var x = 5;
debugger; //break point
myFunc(x);

Testing

There are many many different Javascript unit testing libraries.

QUnit is very easy to get started with because it only requires importing a file, no installations.

QUnit

Template for results page:


    <html>
        <head>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width">
            <title>QUnit Example</title>
            <link rel="stylesheet" href="https://code.jquery.com/qunit/qunit-2.6.1.css">
        </head>
        <body>
            <div id="qunit"></div>
            <div id="qunit-fixture"></div>
            <script src="https://code.jquery.com/qunit/qunit-2.6.1.js"></script>
            <script src="MyTests.js"></script>
            <script src="MyJavascript.js"></script>
        </body>
    </html>

MyTests.js contents:


QUnit.test("sum: positive integers", function( assert ) {
    assert.ok(sum(1, 2) == 3, "Passed!" );
});

MyJavascript.js contents:


function sum(a, b) {
    return a + b;
}

Basic assertions available in QUnit; there are more.


QUnit.test("Title of Test", function( assert ) {
    assert.ok(boolean, "Boolean True Message" );
    
    assert.notOk(boolean, "Boolean False Message" );
    
    assert.equal(actual-value, expected-value, "Values Match Message" );
    
    assert.throws(function-that-throws-exception, expected-exception-type, "Expected Exception Thrown Message" );
});


Interpreter

Errata and oddities about the JavaScript interpreter.

Strict


'use strict';

This is a literal directive that indicates code should be interpreted in strict mode.
It can be specified at the beginning of a file or a function to give scope over the whole file or function.
- if it is specified anywhere else, it will be ignored

Strict mode means:
- cannot use undeclared variables (so you don't accidentally make a new global variable by misspelling a variable name)
- cannot assign values to a non-writable property, a getter-only property, a non-existing property, a non-existing variable, a non-existing object
- cannot delete variables or objects or functions
- cannot duplicate parameter names
- cannot use octal numeric literals (ex: var x = 010;)
- cannot use octal escape characters (ex: var x = "\010";)
- cannot use keyword "eval" as a variable name, nor "arguments", nor any of these keywords
    implements, interface, let, package, private, protected, public, static, yield
- cannot use "with" statement
- "eval" cannot create variables in its scope (ex: eval("var x = 2");)

Hoisting

The JavaScript interpreter will hoist these statements to the top of their scope before running the code:
- variable declarations (but not variable initializations)
- function declarations with their definitions (but not function expressions or arrow functions or constructed functions)

See the sections for Functions and Variables for more details.

Ex

x = 5;
console.log(x);

var x;
is valid because it becomes

var x;
x = 5;
console.log(x);

Only declarations will be hoisted, because the interpreter knows it is not changing logic.
Initializes will not be hoisted.

x = 5;
console.log(x + y);

var x;
var y = 7;
becomes

var x;
x = 5;
console.log(x + y); //y is undefined here

var y = 7;


Promises

Promises are like event listeners, but they can only be triggered once, and they can be triggered by a state that has already occurred.


img1.ready().then(function() {
  // run if image is already loaded, or becomes loaded
}, function() {
  // run if image has failed or does fail to load
});

Promise.all([img1.ready(), img2.ready()]).then(function() {
  // run if all are already loaded, or when they all become loaded
}, function() {
  // run if one or more failed, or when one fails in the future
});

A promise is pending until the action succeeds or fails.
A promise is settled once the action succeeds or fails.
A promise is fulfilled if the action succeeds.
A promise is rejected if the action fails.

Debugging

Console.log


console.log('a'); //outputs 'a'

console.log('a', 'b', 'c'); //outputs 'a b c'
Module

A module is a collection of related code. Modules provide organization reusable code, so you can specify which widgets are needed by each of your projects.

Modules should have as few dependencies as possible. Ideally, a module does not rely on any code outside the module.

Modules provide a private space for variables, so that you do not pollute the global namespace.

Module Pattern

The Module Pattern mimics the idea of classes in Object-Oriented Programming. It allows you to have public and private methods and variables in an object.

An Immediately Invoked Function Expression is one implementation of the Module Pattern (see the Function section below).

Using closures to protect private data is another implementation of the Module Pattern (see the Closure section below).

ES6 introduced native support for modules (called ESM Modules or ECMAScript Modules):

//I'm not sure this example is right

// in file lib/counter.js
var counter = 1;
function increment() { counter++; }
function decrement() { counter--; }

module.exports = {
    counter: counter,
    increment: increment,
    decrement: decrement
};

// in file src/main.js
var counter = require('../lib/counter');
counter.increment();
console.log(counter.counter); //outputs 1

"require" makes a copy of the module, which is disconnected from the original module.

CommonJS is a library for creating modules that can be imported into other projects. You can specify which modules are publicly available with "module.exports" and you can specify which modules to import into your project with "require". CommonJS was created before ESM Modules became available, and is used by Node.js.

CommonJS module:

//in the module file
function myModule()
{
    this.hello = () => 'hello';
    this.goodbye = () => 'goodbye';
}
module.exports = myModule;

//in the project file
require('myModule');

CommonJS only loads modules synchronously, meaning the web page will be blocked while modules are loaded from the server. CommonJS is designed for server-side use.

Canvas

Initialize

HTML

<canvas width='200px' height='200px' id='myCanvas'></canvas>

JS

var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');

Styles

Colors

context.fillStyle = '#FF0000';
context.strokeStyle = '#00FF00';

Solid line (default) or dashed line

context.setLineDash([]); //solid line
context.setLineDash([lineDashLength, lineDashSpacing]); //dashed line

Line width

context.lineWidth = 5;

Shapes


//basic shapes
context.fillRect(25, 25, 100, 100);
context.strokeRect(50, 50, 50, 50);

//custom shapes
context.beginPath();
context.moveTo(75, 50);
context.lineTo(100, 75);
context.lineTo(100, 25);
context.fill(); //fill in the shape

context.beginPath();
context.moveTo(75, 50);
context.lineTo(100, 75);
context.lineTo(100, 25);
context.closePath(); //return to origin
context.stroke(); //draw outline of path

//given circle center (x,y) with radius
//draw arc from start to end radians
context.arc(x, y, radius, startRadians, endRadians); //0 radians = east point, clockwise
context.stroke(); //actually draws the arc outline
context.fill(); //actually draws the arc filled in

Text


//draw text
context.font = "12px Georgia";
context.fillText(elementId, 10, 10);
var textWidth = context.measureText(text).width;

There is not built-in measurement for text height.

Bookmarklet

A way to save a piece of javascript code, and run it against the current web page.

1) Create a browser bookmark
2) Replace the "Location" with pure javascript
3) Click this bookmark to execute the javascript against the current web page

Ex:

javascript:document.getElementsByTagName("table")[0].rows[0].cells[0].remove()
Scrolling

Examples are based on vertical scrolling.

Event:

window.onscroll = function() {};
scrollableElement.onscroll = function() {};

Element position in browser viewport:

var position = element.getBoundingClientRect();
Has a top, left, width, and height.

Element position in scrollable parent viewport:

var elementYInParent = element.getBoundingClientRect().top - element.parentNode.getBoundingClientRect().top;

Total height of content in scrollable parent:

var contentHeight = element.parentNode.scrollHeight;

Total height of scrollable parent viewport:

var viewportHeight = element.parentNode.getBoundingClientRect().height;

Current position of scrollable parent scroll bar:

var position = element.parentNode.scrollTop;

Possible range of parent scroll positions:

var contentHeight = element.parentNode.scrollHeight;
var viewportHeight = element.parentNode.getBoundingClientRect().height;
var minScrollTop = 0;
var maxScrollTop = contentHeight - viewportHeight;

Scroll element into view:

var parent = element.parentNode;
var elementYInParent = element.getBoundingClientRect().top - element.parentNode.getBoundingClientRect().top;
parent.scrollTop += elementYInParent;

Animate scroll instead of jumping:

var animateScrollTarget = null; //so you aren't heading in two directions at once
function animateScroll() {
    if(animateScrollTarget == null)
        return;
    var element = <selector for the scrollable element>;
    if(element.scrollTop == animateScrollTarget)
        return;
    var movementUnit = 15;
    if(Math.abs(element.scrollTop - animateScrollTarget) < movementUnit)
    {
        element.scrollTop = animateScrollTarget;
        return;
    }
    if(element.scrollTop > animateScrollTarget)
        element.scrollTop -= movementUnit;
    else
        element.scrollTop += movementUnit;
    
    setTimeout(function() {
        animateScroll();
    }, movementUnit);
}