javascript_logo.png

Introduction to JavaScript

JavaScript is probably the most popular programming language in the world.

Overview

JavaScript, the language:

JavaScript was originally named Mocha and the first version was written by Brendan Eich in 10 days in May of 1995. The name was changed to LiveScript in September, 1995, and became JavaScript sometime after that.

Although over 20 years old, JavaScript is still an evolving language. A lot of the code in the wild, and a lot of the code you see online and in books, is quite old and may not follow modern best practices. This is just the way the world is.

Also see:

Getting Started

Here’s your first JavaScript program (a one-liner, of course):

console.log("Hello, world.");

This script consists of
one statement which is a call to
the function that is the value of
the property named log of
the property named console of
the global object.

There are at least five ways to run JavaScript code:

  1. Bookmarklet. Type "javascript:" followed by the program into your web browser’s location field.
  2. Runner. Use The JavaScript Runner, JSFiddle, JS Bin, JS.do, or CodePen.
  3. Shell (REPL). Use the developer console that comes with your web browser or the REPL in Node.js.
  4. Embedded in HTML. You can write JavaScript code directly inside of an HTML document:
    <!doctype html>
    <html>
      <head>
        <meta charset="utf-8">
        <title>A Greeting</title>
      </head>
      <body>
        <p>My first JavaScript program:</p>
        <script>
          console.log("Hello, World.");
        </script>
      </body>
    </html>
    

    but this is frowned upon; better is to put it in a separate file and reference the script by filename:

    bmi.html
    <!doctype html>
    <html>
      <head>
        <meta charset="UTF-8">
        <title>BMI Calculator</title>
      <head>
      <body>
        <h1>Find Your BMI</h1>
    
        <p>Weight in pounds <input type="text" id="weight"></p>
        <p>Height in inches <input type="text" id="height"></p>
        <p>The BMI is <span id="bmi"></span></p>
    
        <script src="bmi.js"></script>
      </body>
    </html>
    
    bmi.js
    let poundsBox = document.getElementById("weight");
    let inchesBox = document.getElementById("height");
    let bmiSpan = document.getElementById("bmi");
    
    const KILOS_PER_POUND = 0.453592;
    const METERS_PER_INCH = 0.0254;
    
    function computeBMI() {
      let kilos = +poundsBox.value * KILOS_PER_POUND;
      let meters = +inchesBox.value * METERS_PER_INCH;
      bmiSpan.innerHTML = kilos / (meters * meters);
    }
    
    poundsBox.addEventListener('input', computeBMI);
    inchesBox.addEventListener('input', computeBMI);
    

    bmi.png

    JavaScript programs can run not only in web browsers, but also virtual worlds, Adobe Photoshop, and other environments.

    Exercise: What other host environments have JavaScript programs run in?
  5. On the command line!. Run the program with Node.js. For example:

    powers.js
    const limit = +process.argv[2];
    
    for (let x = 1, i = 0; i <= limit; i += 1) {
      console.log(`2^${i} = ${x}`);
      x += x;
    }
    
    $ node powers.js 5
    2^0 = 1
    2^1 = 2
    2^2 = 4
    2^3 = 8
    2^4 = 16
    2^5 = 32
    
Exercise: (REQUIRED) Install Node.

Data Types

All values belong to one of 7 types:

TypeValues of the Type
UndefinedOnly one value: undefined. Means “I don’t know,” “I don’t care”, or “None of your business.”
NullOnly one value: null. Means “no value.”
BooleanOnly two values: true and false.
NumberThe IEEE 754 floating point values. Values that are integers can be expressed in binary, octal, decimal, or hex; non-integers must be expressed in decimal. Examples:
  • 8
  • 7.23342
  • 6.02e23
  • 0xff3e
  • 0b11010100001010
  • 0o237
  • Infinity
  • NaN
StringSequences of zero or more UTF-16 code units. Examples:
  • "hello"
  • "She said 'I don’t think so 😎'... (╯°□°)╯︵ ┻━┻)"
  • 'x = "4"'
  • "abc\tdef\"\r\nghi\n🏄‍♀️🏀\n"
  • "Olé"
  • "Ol\xe9"
  • 'Will I?\u043d\u0435\u0442\u263a'
  • `The sum of ${x} and ${y} is probably ${x + y}`
Strings are immutable. Only backtick-delimited literals can span lines and support string interpolation.
SymbolUnique things. Every time you create a symbol, you get a new thing, different from all other symbols. Examples:
  • Symbol()
  • Symbol('dog')
  • Symbol('dog') // different from the one above
ObjectEverything that isn’t one of the above six types. Examples:
  • {}
  • {latitude: 74.2, longitude: -153.11}
  • [true, true, {last: false, value: 'okay'}, [0, 0, 2]]
  • new Set([5, 1, 2])
  • new Date(2000, 12, 31)
  • (x, y) => x * x + y * y
  • /Boo+m!?/gi

Some common operations on numbers:

    +x    -x    x + y    x - y    x * y    x / y    x % y    x ** y
    ~x    x << y    x >> y    x >>> y    x & y    x | y    x ^ y
    x.toString()    x.toString(radix)   x.toLocaleString(locale)
    Number.NEGATIVE_INFINITY    Number.POSITIVE_INFINITY
    Number.MIN_VALUE    Number.MAX_VALUE
    Number.MIN_SAFE_INTEGER    Number.MAX_SAFE_INTEGER
    Number.isFinite(x)    Number.isSafeInteger(x)    Number.isInteger(x)    Number.isNaN()
    Math.E    Math.PI    Math.LN2    Math.SQRT2    Math.random()
    Math.floor(x)    Math.ceil(x)    Math.trunc(x)    Math.round(x)
    Math.exp(x)    Math.log(x)
    Math.sqrt(x)    Math.pow(x, y)    Math.hypot(x, y, ...)
    Math.min(x, y, ...)   Math.max(x, y, ...)
    Math.sin(x)    Math.cos(x)    Math.atan2(y, x)

Some common operations on strings:

    String.fromCharCode(c, ...)    String.fromCodePoint(c, ...)
    s[i]    s.charCodeAt(i)    s.codePointAt(i)    s.length
    s.startsWith(t)    s.endsWith(t)    s.includes(t)
    s.indexOf(t)    s.lastIndexOf(t)
    s.slice(i, j)    s.substring(i, j)    s.substr(i, len)
    s.toLocaleLowerCase()    s.toLocaleUpperCase()
    s.concat(s1, s2, ...)    s.repeat(count)    s.split(strOrRegex)    s.trim()
    s.match(regex)    s.search(regex)    s.replace(strOrRegex, strOrFun)

These work on numbers and strings:

    x < y    x <= y    x >= y    x > y

These work on any value:

    x === y     // whether x and y are EQUAL
    x !== y     // !(x === y)
    x == y      // whether x and y are SIMILAR (do not use!!)
    x != y      // !(x == y) (do not use!!)
    x && y      // if x is falsy then x, else y
    x || y      // if x is truthy then x, else y
    !x          // if x is truthy then false, else true

Determining the Type of an Expression

In JavaScript, types are not objects in their own right; instead, if you ask for the type of an object, you will get a string. Unfortunately, the normal way you do this is fairly messed up:

typeof undefined      // 'undefined'
typeof null           // 'object'   -- WTF??? THIS IS A LANGUAGE DESIGN FLAW
typeof false          // 'boolean'
typeof 93.8888        // 'number'
typeof "Hi"           // 'string'
typeof {x: 1, y: 2}   // 'object'
typeof [1, 2, 3]      // 'object'   -- sensible, arrays are just objects
typeof /o*h/          // 'object'   -- sensible, regexes are just objects
typeof (x => x + 1)   // 'function' -- grrrr, functions are actually objects too
typeof NaN            // 'number'   -- sounds hilarious but makes sense

Weak Typing

Usually, if an operator doesn’t get operands of the right type, it does conversions to get arguments of the right types. This means JavaScript is weakly typed. In a strongly typed language, on the other hand, operands of the wrong type generate an error.

When a boolean is expected:

When a number is expected:

When a string is expected:

A value that is false or would be converted to false is called falsy. All other values are truthy. (I did not make up those names.)

Objects

Undefined, Null, Boolean, Number, String, and Symbol are called primitive types. Object is a reference type, meaning object values are references. Objects have properties which are key-value pairs. Create and access the properties with either dot or square bracket notation. You can even delete properties.

let x = {};
x.age = 17;
x.height = 65.3;
let score = x.age * x["height"];
let z = {age: 30, color: "red", total: 3};
z.last = true;
let rat = {triple: {a: 4, b: undefined, c: {4: null}}, 7: "stuff"};
delete z.age;

Notice how the values of primitives are ”stored directly” while the values of objects are the pointers, not the objects themselves:

jsobjects.png

The fact that object values are actually pointers has major ramifications for assignment and equality testing that you absolutely must understand. Study the following animation:

jsobjectsanimation.gif

In detail, here’s what happened:

let a = {x: 3, y: 5};   // The curly braces CREATE a new object
let b = {x: 3, y: 5};   // The curly braces CREATE a new object
let c = b;              // Copy the arrow in box b into the box for c
a !=== b;               // Because these two arrows point to different things
b === c;                // Because these two arrows point to the same thing

There’s some convenient syntax surrounding objects that is good to know:

let x = 10;
let c = {x, y: 3}        // x is the same as x:x, so {x: 10, y: 3}
let o = {a: 5, b: 4, c}; // {a: 5, b: 4, c: {x: 10, y: 3}}
let {b, c: {y}} = o;     // This is called destructuring. Now b is 4 and y is 3

Prototypes

Every object in JavaScript has a prototype. When looking up properties, the prototype chain is searched. Objects created with the { } syntax all get the same built-in prototype (more on this later), but you can alternatively set the prototype when the object is created, with Object.create:

let protoCircle = {x: 0, y: 0, radius: 1, color: "black"};
let c1 = Object.create(protoCircle);
c1.x = 4;
c1.color = "green"; // Now c1.y === 0 and c1.radius === 1

jsobjwithproto.png

Here $c1$ has two own properties: x and color. It has several inherited properties, including y and radius from its own prototype, and a bunch of others from its prototype’s prototype.

Prototypes are very useful when creating a bunch of objects that look or behave similarly:

jsobjswithsharedproto.png

Arrays

An array is an object constructed with square brackets or the new Array syntax. It has a length property as well as non-negative integer properties.

let a = [];
let b = [1, 5, 25];
let c = new Array();                      // Same as []         (DON'T DO THIS)
c = new Array(4, 5, 6);                   // Same as [4, 5, 6]  (DON'T DO THIS)
c = new Array(10);                        // 10 cells, all undefined.
let [d, e, f] = [a.length, b.length, a];  // d===0, e===3, f===[] (destructuring)

let g = [1, true, [1,2], {x:5, y:6}, "Hi"];
let h = {triple: {a:4, b:"dog", c:[1,null]}, 7: "stuff"};
let i = [h.triple.a, h[7]];

g[1];          // true
g[2][0];       // 1
g.length;      // 5
g[10] = 100;   // Makes g[5] through g[9] undefined
g.length;      // 11
g.length = 2;  // Now g is just [1, true]
Exercise: Experiment with arrays. Can we get to the third element of array dogs by saying dogs.2 or dogs["2"]? Or is dogs[2] the only way? What about dogs."2" or let x=2; dogs.x?
Exercise: Experiment some more with arrays. Does let x = ["dog"] define a one element array? What if we create a three-element array and try to print the value of its 12th element? What if we create a three-element array and set the value of its 12th element.
Exercise: What’s going on here?
   let a = new Array(3);
   a[0] = 1;
   a[1] = "hello";
   a[2] = a;

Here are some array operations:

// Construction
const a = [300, 900, 200, 400, 300, 700];
const b = [500, 300];
const c = Array.of(95, 33, 'dog', false); // [95, 33, 'dog', false]
const d = Array.from('dog');              // ['d', 'o', 'g']
const e = [5, ...b, -10]                  // [5, 500, 300, -10] (see the splat?)
const f = [b, ...b]                       // [[500, 300], 500, 300]

// Tests
Array.isArray(['x', true, 3]);            // true
Array.isArray({0: 'x', 1: true, 2: 3});   // false

// Accessors (these do not change their operands)
a.includes(400)      // true
a.includes(7)        // false
a.indexOf(300)       // 0
a.lastIndexOf(300)   // 4
a.indexOf(2500)      // -1
a.slice(2, 5)        // [200, 400, 300]
a.slice()            // makes a shallow copy!
a.concat(b)          // [300, 900, 200, 400, 300, 700, 500, 300]
a.join('--')         // "300--900--200--400--300--700"
a.toString()         // '300,900,200,400,300,700'

// Mutators
a.push(3)            // returns 7, a is now [300, 900, 200, 400, 300, 700, 3]
a.pop()              // returns 3, a is now [300, 900, 200, 400, 300, 700]
a.unshift(8)         // returns 7, a is now [ 8, 300, 900, 200, 400, 300, 700 ]
a.shift()            // returns 8, a is now [300, 900, 200, 400, 300, 700]
a.reverse()          // a is now [ 700, 300, 400, 200, 900, 300 ]
a.sort()             // a is now [ 200, 300, 300, 400, 700, 900 ]
a.fill(9)            // a is now [ 9, 9, 9, 9, 9, 9 ]
e.splice(1, 2, 90, 70, 200, 6, 17);
                     // At position 1, remove 2 elements, and insert the others
                     // e is now [ 5, 90, 70, 200, 6, 17, -10 ]
e.copyWithin(1, 4, 6)
                     // shallow copy elements at positions [4, 6) to position 1
                     // e is now [ 5, 6, 17, 200, 6, 17, -10 ]
Exercise: Experiment with slice. Show that you can use it for inserting elements only, deleting elements only, or replacing elements only.
Exercise: Experiment with copyWithin. What if the target range overlaps the source range? What if (any of) the indexes are out of bounds? How are negative indexes handled?

You will sometimes see Array.from when programming in the web browser. Things like NodeLists and HTMLCollections look like arrays but they aren’t; use Array.from to turn them into real arrays if you need to do such things.

Functions

Functions are either arrow functions or non-arrow functions. Here are some arrow functions in action:

(x => x * 2 + 1)(20)                                    // evaluates to 42
const square = x => x * x;                              // can also use let or var
const now = () => new Date();
let hypotenuse = (x, y) => (Math.sqrt(x * x + y * y))
hypotenuse(3, -4)                                       // evaluates to 5

Non-arrow function values use the function keyword, the Function constructor, or are created in a function declaration. When inside an object, you can leave off the function word:

function successor (x) {
  return x + 1;
}
const sum = function (x,y) {return x + y;}
const predecessor = new Function("x", "return x - 1;"); // Don’t actually use this

const x = {
  f: (a) => a * 3,                  // arrow function
  g: function (a) {return a * 3;},  // NOT an arrow function
  h(a) {return a * 3;}              // NOT an arrow function
}

A function value is fully first-class in JavaScript meaning:

Furthermore a function is an object so:

Functions that are passed and returned are called higher order functions. This should be no big deal. We had higher order functions in the 1950s in Lisp, but it took many decades for the whole world to realize how awesome this was.

const plusSix = x => x + 6;
const squared = x => x * x;
const twice = (f, x) => f(f(x));
twice(plusSix, 5);                           // 17
twice(squared, 4);                           // 256
const compose = (f, g) => (x => g(f(x)));
const squareThenPlusSix = compose(squared, plusSix);
squareThenPlusSix(10);                       // 106
compose(plusSix, squared)(5);                // 121
`twice expects ${twice.length} arguments`;   // 'twice expects 2 arguments'

Parameters

Passing an argument to a parameter is just like assignment. Nothing more, nothing less. But there are interesting things:

Scope

Variables declared with var are local to the innermost enclosing function. When declared with const or let, they are local to the innermost enclosing block.

Variables are visible within their scope (this includes blocks and functions nested within that scope) and are invisible outside:

var a = 1;
let b = 2;
// c, d, e, and f are not in scope here
function example() {
  console.log(c); // undefined
  // If we tried to use d here, it would throw a ReferenceError
  var c = 3;
  let d = 4;
  console.log(e); // undefined
  // If we tried to use f here, it would throw a ReferenceError
  if (true) {
    var e = 5;
    let f = 6;
    console.log(a, b, c, d, e, f);
  }
  console.log(e); // 5
  // f out of scope, using it would throw a ReferenceError
}
// Only a and b are in scope here; c, d, e and f are no longer in scope
example();

Did you read that example carefully? Note that using a let or const variable in its scope but before the declaration throws a ReferenceError. Doing that with a var variable just gives you undefined.

Here’s a major ramification of function scope versus block scope:

let a = [];
for (var i = 0; i < 10; i++) { a[i] = () => i; }
a[3]();                                              // 10, GRRRRR
for (let i = 0; i < 10; i++) { a[i] = () => i; }
a[3]();                                              // 3, YES I EXPECTED THAT
Exercise: Study that example.
NEVER USE var.

The var keyword is obsolete. Never use it. You should know how it works though, because you might see it in other people’s code.

Closures

When an inner function is sent outside its enclosing function, it is called a closure (because the outer variables are retained and “close over” the function). Closures are very popular in JavaScript because local variables are one of the very few ways to do information hiding.

You will often see code like this:

const nextFib = (() => {
  let [a, b] = [0, 1];
  return () => {
    [a, b] = [b, a + b];
    return a;
  }
})();

nextFib();    // 1
nextFib();    // 1
nextFib();    // 2
nextFib();    // 3
nextFib();    // 5
nextFib();    // 8

Generators

There’s another way to do that previous example. It’s a little weirder. Learn both ways.

function* fibGenerator() {
  let [a, b] = [0, 1];
  while (true) {
    [a, b] = [b, a + b];
    yield a;
  }
}

const fib = fibGenerator();
fib.next();    // { value: 1, done: false }
fib.next();    // { value: 1, done: false }
fib.next();    // { value: 2, done: false }
fib.next();    // { value: 3, done: false }
fib.next();    // { value: 5, done: false }
fib.next();    // { value: 8, done: false }

this

The expression this evaluates to different things based on what the heck is going on at run time.

  1. If used in a top-level statement outside of any function, or at the top-level statement in a function called as a global function, it refers the global object.
    a = 3;
    this.a;                      // 3
    this.a = 2;
    a;                           // 2
    function f() {this.a = 10}
    f();
    a;                           // 10
    
  2. If used in a function f that was called call to a function made with one of the function methods apply, call, or bind, the value for this was “passed in” somehow. (Note: there are other places in JavaScript where you can “pass in” a value for this, but these are the three main ones, perhaps.)
    let a = {x: 1, y: 3}
    function f(c, s) {this.x = this.x * c + s;}
    
    f.apply(a, [3, 5]);
    a;                           // { x: 8, y: 3 }
    f.call(a, 4, 1);
    a;                           // { x: 33, y: 3 }
    f.bind(a)(3, 8);
    a;                           // { x: 107, y: 3 }
    
  3. If called within a non-arrow function f that is a property of an object, and called in the expression obj.f(), it refers to obj, which we call the receiver, and the function is called a method.
    let x = {a: 1, b: function (x) {return x + this.a;}};
    x.b(2);                                    // 3
    
    // Wait we should use a more modern syntax
    let x = {a: 1, b(x) {return x + this.a;}};
    x.b(2);                                    // 3
    
    // The expression is evaluated dynamically, not statically
    let f = function (x) {return x + this.a;}
    let x = {a: 10, b: f};
    x.b(2);                                    // 12.
    
    // Hey let's bundle up methods with our data!
    let p = {
      x: 0,
      y: 0,
      move(dx, dy) {this.x += dx; this.y += dy;},
      reflect() {this.x = -this.x; this.y = -this.y;}
    };
    p.move(5, 4);
    p.reflect();
    [p.x, p.y];      // [-5, -4]
    
  4. If used in a function invoked with the new operator, it refers to the object being created. Let’s get to this now.

Constructors

The previous example made a point object containing data fields and methods. If we want to make millions of point objects, each one of them would have copies of the move and reflect functions. If you care about saving memory, we should put those functions in a prototype. Interestingly, Eich, way back in 1995, created an operator, called new, that facilitates a pattern of creating objects with a shared prototype. Here’s how it works:

function Point(x = 0, y = 0) {
  this.x = x;
  this.y = y;
};
Point.prototype.move = function (dx, dy) {this.x += dx; this.y += dy;}
Point.prototype.reflect = function () {this.x = -this.x; this.y = -this.y;}
Point.prototype.toString = function () {return `(${this.x},${this.y})`;}

let p = new Point(3, 7);  // THE new IS CRUCIAL!

When you call the function with new, the function you call behaves as a constructor which means:

This works because of something interesting: every function you create has two properties: length, which holds the number of parameters for the function, and prototype, which is an object with a constructor property, whose value is the function itself.

jspoints.png

Exercise: We know that the prototype chain is searched when we try to read a property from an object that doesn’t have the property. But what happens if you try to write a property that doesn’t exist in an object, but does exist in an object higher in the prototype chain?

Class Syntax

There is a keyword class which you can use to build exactly the same structure we built above.

class Point() {
  constructor(x = 0, y = 0) {
    this.x = x;
    this.y = y;
  }
  move(dx, dy) {this.x += dx; this.y += dy;}
  reflect() {this.x = -this.x; this.y = -this.y;}
  toString() {return `(${this.x},${this.y})`;}
};

let p = new Point(3, 7);  // THE new IS CRUCIAL!

This code creates a function called Point whose body is the constructor, and it assigns those three functions to Point.prototype. Please keep in mind that using the class keyword is nothing more than syntactic sugar for functions and prototypes. There are no such thing as classes in JavaScript. Only functions.

Exercise: Try out this code. Evaluate typeof Point. You got "function", right?

Prototype Chains

The class syntax has a little feature to automatically make prototype chains, which is a way of doing inheritance. Here’s an example:

animals.js
class Animal {
  constructor(name) { this.name = name; }
  speak() { return `${this.name} says ${this.sound()}`; }
}

class Cow extends Animal {
  sound() { return 'moooo'; }
}

class Horse extends Animal {
  sound() { return 'neigh'; }
}

class Sheep extends Animal {
  sound() { return 'baaaa'; }
}

const animals = [new Horse('CJ'), new Cow('Bessie'), new Sheep('Little Lamb')];
animals.forEach(a => console.log(a.speak()));
console.log(animals[Math.floor(Math.random() * animals.length)].sound());
Exercise: Draw a picture of this object family.
These so-called “deep class hierarchies” are frowned upon by some folks in the JavaScript community. But go ahead and use them if you know what you are doing and you have one of those rare cases where it makes sense for your application.

The Big Deal about Arrow vs. Non-Arrow Functions

Did you catch, in our explanation of the this expression, that this is bound to the receiver only for non-arrow functions? That’s a big deal, since it means methods should never use arrows:

const counter = {
  val: 0,
  inc() { this.val++; },       // This does what you want
  dec: () => { this.val--; }   // NOOOOOOO THIS WON'T WORK
};

It’s a big deal, too, for functions nested within methods. You should use arrows for these, so that the this doesn’t get hijacked:

const counter = {
  val: 10,
  countDown() {
    if (this.val > 0) setTimeout(() => {console.log(--this.val); this.countDown();}, 1000);
  }
};
Exercise: What would happen if you replaced the first argument of setTimeout with function () {console.log(--this.val); this.countDown();}?
Remember, arrow functions don’t get this mapped to their receiver.

When using methods, i.e., functions inside an object that need to refer to the object, you want the function syntax. You can use the arrows everywhere else.

Crockford doesn’t use this, new, or prototypes

Douglas Crockford suggests that you do things like this:

classless.js
function circle(spec) {
  let { radius } = spec;
  let area = () => Math.PI * radius * radius;
  let expand = (factor) => { radius *= factor; };
  let toString = () => `Circle with radius ${radius}`;
  return Object.freeze({ area, expand, toString });
}

function rectangle(spec) {
  let { width, height } = spec;
  let area = () => width * height;
  let transpose = () => { [width, height] = [height, width]; }
  let toString = () => `Rectangle ${width} x ${height}`;
  return Object.freeze({ area, transpose, toString });
}

const c = circle({ radius: 10 });
const r = rectangle({ height: 8, width: 20 });
console.log(c.toString());
c.expand(3);
console.log(c.toString());
console.log(c.area());
console.log(r.toString());
r.transpose();
console.log(r.toString());

Kind of nice, but remember each object has its own copy of each of its methods, so don’t go using this technique when you are creating millions of objects with the same set of methods.

And here’s a neat little article on avoiding this (by someone other than Crockford). It explains how using this just adds a lot of cognitive load that we really don’t need, and how to avoid issues by using only pure functions and objects.

Regular Expressions

JavaScript has special syntactic sugar for regular expressions, for example

/dog/
/JavaScript/i
/moe|larry|curly/i
/colo(u)?r/
/<([^>]*)>[^<]*<\/\1>/
/\d{5}(-\d{4})?/

JavaScript’s regular expression language is pretty standard:

Two ways to create:

re = /a+bc/;
re = new RegExp("a+bc");

JavaScript doesn’t have as rich a regex language as most other languages, but it has enough for most cases. The complete reference is here. In practice, you will want to grab a third-party module like xregexp.

Methods:

regex.exec(str)
If there’s a match, returns an array of match info. If no match, returns null.
regex.test(str)
Simply returns true if there’s a match and false otherwise.
str.match(regex)
Without the g modifier, same as regex.exec(str). With the g modifier, returns an array of all matches.
str.search(regex)
Returns the index of the beginning of the match, or -1 if no match.
str.replace(regex, newTextOrFunction)
Replaces the matched part of the string with new text.
str.split(strOrRegex)
Returns an array of substrings split by the regex separator.

Important:

Metaprogramming

There is a lot of disagreement about what actually constitutes metaprogramming, but for these notes lets use the very loose definition of operating on program constructs as opposed to operating on application-level objects. For example, programming would involve asking “how old is this person?” or “what is the shortest path from Los Angeles to Madrid?” and metaprogramming might ask “what are the properties of this object and which are writable?”, “change the prototype this object to something else,” or “manufacture some new code at runtime from a string and execute it.”

Object Introspection and Self-Modification

You can inquire about, access, and modify an object’s prototype:

let basicCircle = {x:0, y:0, r:1}
let c = Object.create(basicCircle)
c.x = 2
c.color = 'blue'
Object.getPrototypeOf(c)                          // basicCircle
Object.getPrototypeOf(basicCircle)                // Object.prototype
Object.prototype.isPrototypeOf(basicCircle)       // true
basicCircle.isPrototypeOf(c)                      // true
let anotherCircle = {x: 10, y: 3}
Object.setPrototypeOf(anotherCirce, basicCircle)  // but this is slow

You can inquire about, access, and modify an object’s properties:

Object.keys(c)                             // [ 'x', 'color' ]
Object.getOwnPropertyNames(c)              // [ 'x', 'color' ]
let a = []; for (let p in c) a.push(p); a  // [ 'x', 'color', 'y', 'r' ]
c.hasOwnProperty('color')                  // true
c.hasOwnProperty('y')                      // false

You can inquire about and change the way properties are allowed to be used:

Object.getOwnPropertyDescriptor(c, 'x')
    // { value: 2,
    //   writable: true,
    //   enumerable: true,
    //   configurable: true }
c.x = 10
c.x                                                  // 10
Object.defineProperty(c, 'x', {writable: false})
Object.getOwnPropertyDescriptor(c, 'x')
    // { value: 10,
    //   writable: false,
    //   enumerable: true,
    //   configurable: true }
c.x = 500                                            // no error, but ...
c.x                                                  // 10

Good to know: The attributes depend on how the object is created:

a = {x: 1, y: 2}
Object.getOwnPropertyDescriptor(a, 'x')
    // { value: 1,
    //   writable: true,
    //   enumerable: true,
    //   configurable: true }
b = Object.create(Object.prototype, {x: {value: 1}, y: {value: 2}})
Object.keys(b)                            // []
Object.getOwnPropertyNames(b)             // [ 'x', 'y' ]
Object.getOwnPropertyDescriptor(b, 'x')
    // { value: 1,
    //   writable: false,
    //   enumerable: false,
    //   configurable: false }

Background information: A property can have either a data descriptor OR an access descriptor.

What do these mean?

See the MDN page for defineProperty for great examples.

You can even change how an object itself is allowed to be modified:

You can inquire which objects exist in your program. Just get a handle on the global object and list its properties.

eval

Calling eval(s) evaluates the string s as JavaScript code.

Aside from a tiny number of legitimate, and very advanced, use cases, this function is a major security risk and never almost never be used. Before you try to use it, look for an alternative.

Read more about eval at Axel Rauschmayer’s blog.

Proxies

Read a fabulous overview by Axel Rauschmayer.

Modules

For a fantastic overview, read Rauschmayer’s book chapter.

JavaScript’s offical module system was created long after people created their own. In fact, the module system used by npm (the largest module ecosystem for any language anywhere on the planet) uses a completely incompatible module system, called CommonJS.

Statements

A program is a sequence of statements and function declarations. Function declarations begin with function, function*, async function, async function*, or class. The statements are:

Keywords

Keywords are those words that may not be used as identifiers:

await break case catch class const continue debugger default delete do else export extends finally for function if import in instanceof new return super switch this throw try typeof var void while with yield

Future Keywords may be used in the future as keywords:

enum

The following are essentially reserved due to static semantic restrictions in some contexts:

let static implements package protected interface private public

Operators

From highest to lowest precedence:

Operators Associativity Description
.
[]
new
without arguments
L member (rhs cannot start with a digit)
member
create instance
()
new
with arguments
N/A call
create instance
++
--
N/A postfix increment
postfix decrement
!
~
-
+
++
--
typeof
void
delete
R logical not
bitwise not
unary negation
unary plus
prefix increment
prefix decrement
type name
evaluate and return undefined
delete
** L exponentiation
*
/
%
L multiply
divide
remainder
+
-
L add
subtract
<<
>>
>>>
L left shift
arithmetic right shift (sign fill)
logical right shift (zero fill)
<
<=
>
>=
in
instanceof
L less than
less than or equal
greater than
greater than or equal
has property
has type
==
!=
===
!==
L similar to
not ==
equals (and same type)
not ===
& L bitwise and
^ L bitwise xor
| L bitwise or
&& L short-circuit logical and
|| L short-circuit logical or
?: R conditional
=
+= -=
*= /= %=
<<= >>= >>>=
&= ^= |=
R assignment
yield
yield*
N/A return from generator
delegate to another generator
... N/A spread
, L comma

Good to know:

Built-in Objects

JavaScript, the language, tracks the ECMAScript Scripting Language Specification, so all of the standard objects in the ECMAScript specification are available to your JavaScript programs. Here are those objects, with a representative set of their properties, as of ES7 (The 7th edition of the ECMAScript Specification, a.k.a. ES2016):

NamePrototypeProperties
The global
object
(unnamed)
Implemen-
tation
dependent
Infinity NaN undefined eval() isFinite() isNaN() parseFloat() parseInt() decodeURI() decodeURIComponent() encodeURI() encodeURIComponent() Math JSON Reflect Object() Function() Boolean() Number() String() Symbol() Array() Float32Array() Float64Array() Int8Array() Int16Array() Int32Array() Uint8Array() Uint8ClampedArray() Uint16Array() Uint32Array() ArrayBuffer() DataView() Date() RegExp() Set() WeakSet() Map() WeakMap() Proxy() Promise() Error() SyntaxError() RangeError() TypeError() ReferenceError() URIError() EvalError()
ObjectFunction.
prototype
prototype create() assign() is() getPrototypeOf() setPrototypeOf() defineProperties() defineProperty() getOwnPropertyDescriptor() getOwnPropertyNames() getOwnPropertySymbols() keys() preventExtensions() isExtensible() seal() isSealed() freeze() isFrozen()
Object.prototypenullconstructor toString() toLocaleString() valueOf() hasOwnProperty() isPrototypeOf() propertyIsEnumerable()
MathObject.
prototype
E LN10 LN2 LOG2E LOG10E PI SQRT1_2 SQRT2 abs() acos() acosh() asin() asinh() atan() atanh() atan2() cbrt() ceil() clz32() cos() cosh() exp() expm1() floor() fround() hypot() imul() log() log1p() log10() log2() max() min() pow() random() round() sign() sin() sinh() sqrt() tan() tanh() trunc()
JSONObject.
prototype
parse() stringify()
ReflectObject.
prototype
getPrototypeOf() setPrototypeOf() construct() ownKeys() has() defineProperty() getOwnPropertyDescriptor() get() set() apply() deleteProperty() isExtensible() preventExtensions()
FunctionFunction.
prototype
length prototype
Function.prototypeObject.
prototype
constructor toString() apply() call() bind()
Function instancesFunction.
prototype
name length prototype
BooleanFunction.
prototype
prototype
Boolean.prototypeObject.
prototype
constructor thisBooleanValue() toString() valueOf()
NumberFunction.
prototype
prototype NEGATIVE_INFINITY POSITIVE_INFINITY MIN_VALUE MAX_VALUE isFinite() EPSILON isInteger() MIN_SAFE_INTEGER MAX_SAFE_INTEGER isSafeInteger() NaN isNaN() parseInt() parseFloat()
Number.prototypeObject.
prototype
constructor toString() toLocaleString() valueOf() toFixed() toExponential() toPrecision()
StringFunction.
prototype
prototype fromCharCode() fromCodePoint() raw()
String.prototypeObject.
prototype
constructor charAt() charCodeAt() codePointAt() startsWith() endsWith() includes() indexOf() lastIndexOf() slice() substring() toLowerCase() toUpperCase() toLocaleLowerCase() toLocaleUpperCase() normalize() localeCompare() trim() concat() repeat() match() search() replace() split() toString() valueOf()
String instancesString.
prototype
length
SymbolFunction.
prototype
prototype for() keyFor() hasInstance() isConcatSpreadable iterator() match() replace() search() species() split() toPrimitive() toStringTag unscopables
Symbol.prototypeObject.
prototype
constructor toString() valueOf()
RegExpFunction.
prototype
prototype
RegExp.prototypeObject.
prototype
constructor flags global ignoreCase unicode multiline sticky source test() exec() toString()
RegExp instancesRegExp.
prototype
lastIndex
DateFunction.
prototype
prototype parse UTC now
Date.prototypeObject.
prototype
constructor getDate() getTime() getFullYear() getMonth() getDay() getHours() getMinutes() getSeconds() getMilliseconds() getTimezoneOffset() getUTCDate() getUTCFullYear() getUTCMonth() getUTCDay() getUTCHours() getUTCMinutes() getUTCSeconds() getUTCMilliseconds() setDate() setTime() setFullYear() setMonth() setHours() setMinutes() setSeconds() setMilliseconds() setUTCDate() setUTCFullYear() setUTCMonth() setUTCHours() setUTCMinutes() setUTCSeconds() setUTCMilliseconds() toString() toISOString() toUTCString() toDateString() toTimeString() toLocaleString() toLocaleDateString() toLocaleTimeString() toJSON() valueOf()
ArrayFunction.
prototype
prototype isArray() of() from()
Array.prototypeObject.
prototype
constructor keys() values() entries() includes() find() findIndex() indexOf() lastIndexOf() slice() every() some() forEach() map() filter() reduce() reduceRight() pop() push() shift() unshift() copyWithin() join() concat() fill() sort() reverse() splice() toLocaleString() toString()
Array instancesArray.
prototype
length
Int8Array
Uint8Array
Uint8ClampedArray
Int16Array
Uint16Array
Int32Array
Uint32Array
Float32Array
Float64Array
Function.
prototype
prototype BYTES_PER_ELEMENT of() from()
{Int8Array
 Uint8Array
 Uint8ClampedArray
 Int16Array
 Uint16Array
 Int32Array
 Uint32Array
 Float32Array
 Float64Array}.
  prototype
Object.
prototype
constructor BYTES_PER_ELEMENT buffer byteLength byteOffset length keys() values() entries() includes() find() findIndex() indexOf() lastIndexOf() slice() every() some() forEach() map() filter() reduce() reduceRight() copyWithin() join() fill() sort() reverse() set() subarray() toLocaleString() toString()
SetFunction.
prototype
prototype
Set.prototypeObject.
prototype
constructor size has() keys() values() entries() add() clear() delete() forEach()
ArrayBufferFunction.
prototype
prototype isView
ArrayBuffer.prototypeObject.
prototype
constructor byteLength slice()
DataViewFunction.
prototype
prototype
DataView.prototypeObject.
prototype
constructor buffer byteLength byteOffset getInt8() setInt8() getInt16() setInt16() getInt32() setInt32() getUint8() setUint8() getUint16() setUint16() getUint32() setUint32() setFloat64() setFloat32() getFloat64() getFloat32()
WeakSetFunction.
prototype
prototype
WeakSet.prototypeObject.
prototype
constructor has() add() has() delete()
MapFunction.
prototype
prototype
Map.prototypeObject.
prototype
constructor size has() get() set() keys() values() entries() clear() delete() forEach()
WeakMapFunction.
prototype
prototype
WeakMap.prototypeObject.
prototype
constructor has() get() set() delete()
ProxyFunction.
prototype
revocable
Proxy.prototypeObject.
prototype
TODO
PromiseFunction.
prototype
prototype all() race() resolve() reject()
Promise.prototypeObject.
prototype
constructor then() catch()
ErrorFunction.
prototype
prototype
Error.prototypeObject.
prototype
constructor name message toString()
EvalError
RangeError
ReferenceError
SyntaxError
TypeError
URIError
Function.
prototype
prototype
{EvalError
 RangeError
 ReferenceError
 SyntaxError
 TypeError
 URIError}.
  prototype
Error.
prototype
constructor name message

JavaScript in Practice

There’s JavaScript the language, and then there’s all the culture around it, which is pretty much inseparable. To use JavaScript in real life, this cultural awareness is crucial.

Versions of JavaScript

What people call the different “versions” of JavaScript vary a little bit, but it’s a good bet people agree on these versions:

Check Kangax's Compatibility Tables to see which implementations support which features of the different versions. Note the radical differences!

Host Objects

JavaScript programs run inside a host environment, which exposes its own global objects. Most web browser environments provide:

Anchor        Applet   Area         Arguments   Button     Checkbox   Crypto
Document      Event    FileUpload   Form        Frame      Hidden     History
HTMLElement   Image    Input        Layer       Link       Location   MimeType
Navigator     Option   Password     Plugin      Radio      Reset      Screen
Select        Style    Submit       Text        TextArea   Window     XMLHttpRequest

On the web, all of the elements (including images and video), attributes, and other pieces of the a web page are part of the Document Object Model or DOM. These are all visible to JavaScript, so most JavaScript programs essentially do graphics by manipulating the DOM (as in the Temperature Conversion script at the beginning of these notes). Unfortunately there are quite a few differences between browsers when it comes to the DOM.

Graphics

In additional to simply manipulating the DOM, there are two other ways to do graphics in JavaScript:

Server Side

Node.js is the dominant, if not the only choice here. Understanding and using and mastering Node is a topic all in itself! It’s an system built on top of the V8 JavaScript Engine. It provides its own global variables, including:

console    setTimeout       Buffer         escape
process    clearTimeout     ArrayBuffer    unescape
module     setInterval      DataView
require    clearInterval    Intl
global     setImmediate
root       clearImmediate

And it comes with a number of built-in modules, including: assert, buffer, child_process, cluster, crypto, dgram, dns, domain, events, fs, http, https, net, os, path, punycode, querystring, readline, repl, stream, string_decoder, tls, tty, url, util, v8, vm, and zlib.

And it hosts an ecosystem of packages, called npm, the largest collection of open source packages in the world (by far!).

JavaScript Libraries

Most serious JavaScript is done with third-party libraries that are not only full of functionality, like special effects (fading, animation, etc.) but that wonderfully hide all the cross-browser differences from the programmer. Then there are the frameworks, gazillions of them.

Probably the major JavaScript libraries and frameworks are:

You might like A Commentary on Google Closure Library.

Unit Testing

I have a separate page of notes for this.

Some History

Interested in some history?

By the way, JavaScript is not like the other language with a similar name.

Wat

JavaScript is awesome, for the most part, but sometimes you need to laugh at its faults. Watch Gary Bernhardt’s famous Wat talk.