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 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. Get over it.

Also see:

Getting Started

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

alert("Hello, world.");

This program (a.k.a. script) consists of
one statement which is a call to
the function that is the value of
the property named alert 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>
        <title>A Greeting</title>
      </head>
      <body>
        <p>My first JavaScript program:</p>
        <script>
          alert("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:

    temperature.html
    <!doctype html>
    <html>
      <head>
        <meta charset="UTF-8"/>
        <title>JavaScript Temperature Converter</title>
      <head>
      <body>
        <h1>Temperature Conversion</h1>
        <p>
          <input type="text" id="temperature">
          <input type="button" id="f_to_c" value="F to C">
          <input type="button" id="c_to_f" value="C to F">
        </p>
        <p id="result"></p>
        <script src="temperature.js"></script>
      </body>
    </html>
    
    temperature.js
    function report(celsius, fahrenheit) {
      document.getElementById("result").innerHTML = `${celsius}°C = ${fahrenheit}°F`;
    };
    
    document.getElementById("f_to_c").onclick = () => {
      let f = document.getElementById("temperature").value;
      report((f - 32) / 1.8, f);
    };
    
    document.getElementById("c_to_f").onclick = () => {
      let c = document.getElementById("temperature").value;
      report(c, 1.8 * c + 32);
    };
    

    temperature-screenshot.png

    BTW 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, say, node.js. For example:

    powers.js
    var limit = +process.argv[2];
    
    for (var x = 1, i = 0; i <= limit; i += 1) {
      console.log('2^%d = %d', 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
    

Basic Concepts

The language architecture is rather simple:

That’s it. Things that seem like basic concepts, like functions, arrays, and regular expressions are just kinds of objects, though syntactically sugared.

Data Types

There are 7 types:

TypeValues of the Type
Undefined Only one value: undefined.
Null Only one value: null.
Boolean Only two values: true and false.
Number The IEEE 754 floating point values, expressible in decimal or hex. (JavaScript also supports octal literals but ECMA-262 ed. 3 does not.) Examples:
  • 8
  • 7.23342
  • 6.02e23
  • 0xff
  • Infinity
  • NaN
String Quote-delimited strings of zero or more UTF-16 code units. Examples:
  • "hello"
  • "She said 'nyet'"
  • 'x = "4"'
  • "abc\tdef\"\r\nghi"
  • "Olé"
  • "Ol\xe9"
  • 'Will I?\u043d\u0435\u0442\u263a'
(Strings are immutable, and can be operated on with ==, !=, <, <=, >=, >, and +.)
Symbol Unique 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
Object Everything that isn’t one of the above six types.

The first six above are called primitive types, and their values can be stored directly. Object values are always accessed through references.

Normally, operators expect operands of a certain type. If an operator doesn’t get operands of the right type, it does conversions to get similar arguments of the right types. This means JavaScript is weakly typed. In a strongly typed language, on the other hand, finding a value of the wrong type generates an error.

When a boolean is expected:

When a number is expected:

When a string is expected:

NOTE: The == and != operators tell you whether their operands are similar-to or not-similar-to each other. Similarity is not the same as equality. These operators are crap and you should never use them. If you want to determine equality, use use === and !== instead. They tell you whether $x$ and $y$ have the same value and the same type.

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.)

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:

Objects

Objects have properties. Access the properties with either dot or square bracket notation. Note: You don’t make a class and declare the properties; you just start using them. 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;

jsobjects.png

Prototypes

Every object in JavaScript has a prototype. When looking up properties, the prototype chain is searched. You can 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

proto1.png

Here c1 has two own properties: x and color. It has two inherited properties: y and radius.

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

proto2.png

Arrays

An array is a kind of object with "numeric properties." Fortunately JavaScript provides a convenient syntax for them, too:

let a = [];
let b = [1, 5, 25];
let c = new Array();        // Lame way to say []
let d = new Array(4, 5, 6); // Lame way to say [4, 5, 6]
let e = new Array(10);      // 10 cells, all undefined.
let f = a.length;
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]];

alert(g[1]);      // Alerts true.
alert(g[2][0]);   // Alerts 1.
alert(g.length);  // Alerts 5.
g[10] = 100;      // Makes g[5] through g[9] undefined.
alert(g.length);  // Alerts 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;

Functions

You can define a function three ways

function successor (x) {
    return x + 1;
}

const sum = function (x,y) {return x + y;}
// can also define with let or var

const predecessor = new Function("x", "return x - 1;");
// or let or var

The first one is a function declation; the latter two make it clear that a function is just an object. The third one, though interesting, is very slow (why?) and considered evil.

Because a function is an object,

Examples:

const plusSix = function (x) {return x + 6;};
const squared = function (x) {return x * x;};
const twice = function (f, x) {return f(f(x));};
alert(twice(plusSix, 5));
alert(twice(squared, 4));
const compose = function (f, g) {return function (x) {return g(f(x));}}
const squareThenPlusSix = compose(squared, plusSix);
alert(squareThenPlusSix(10));
alert(compose(plusSix, squared)(5));
alert("twice expects " + twice.length + " arguments");

Variables declared within a function are local to that function. They are visible everywhere within the function and invisible outside. In particular they are visible to inner functions. When an inner function is sent outside its enclosing function (such as the return value for compose above, 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 really the only way to do "information hiding."

You will often see code like this:

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

this

The special keyword this has one of four different meanings.

  1. If used in a global scope, it refers the global object
  2. If used in a call to a function made with one of the function methods apply, call, or bind, the value for this was “passed in” somehow. (The idea of passing in a value for this is used in quite a few more places, too.)
  3. If used in a function invoked with the new operator (we’ll get to this later) it refers to the object being created.
  4. If called like obj.f(), it refers to x, and the function is called a method.
let x = {a: 1, b: function (x) {return x + this.a;}};
alert(x.b(2));                                         // Alerts 3

The expression this is evaluated dynamically, not statically:

let f = function (x) {return x + this.a;}
let x = {a: 10, b: f};
alert(x.b(2));                               // Alerts 12.

This is starting to look useful...

let p = {
    x: 0,
    y: 0,
    move: function (dx, dy) {this.x += dx; this.y += dy;},
    reflect: function () {this.x = -this.x; this.y = -this.y;}
};
p.move(5, 4);
p.reflect();
alert(p.x + "," + p.y);      // Alerts -5,-4

This made something that looks like a point object. What if we want a whole bunch of points? We should write a function that creates them.

Constructors

The best way to create objects with a prototype is to use Object.create. But this isn’t supported on every browser yet! So you should learn about the funky constructor mechanism, with operator new. It’s this horrible mix of prototypal and classical inheritance. Crockford says "JavaScript is confused about its prototypal nature." No kidding. Check this out:

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

let p = new Point();   // The 'new' is crucial!
p.move(52, 40);
p.reflect();
alert(p.x + "," + p.y);

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

That constructor we just wrote isn’t very good because

One way (but perhaps not the best way) to fix this is to add global functions (ugh) and assign these to the method properties:

// This uses global variables (BAD IDEA!!!!) to try to share functions
let point_move = function (dx, dy) {this.x += dx; this.y += dy;}
let point_reflect = function () {this.x = -this.x; this.y = -this.y;}
let point_to_string = function () {return "(" + this.x + "," + this.y + ")";}

let Point = function (x, y) {
    this.x = x || 0;
    this.y = y || 0;
    this.move = point_move;
    this.reflect = point_reflect;
    this.toString = point_to_string;
};

let p = new Point();
let q = new Point(3, 7);
p.move(52, 40);
p.reflect();
q.move(1, 1)
alert(p + " and " + q);
Exercise: Explain the "|| 0" idiom.

Now suppose you wanted to add a "move_to_origin" method. You could add it to just one object:

    p.moveToOrigin = function () {this.x = 0; this.y = 0;}
    p.moveToOrigin();
    q.moveToOrigin(); // ERROR: q has no moveToOrigin property
    alert(p + " and " + q);

But to add it to every point object, even those not yet created, you can rewrite the constructor, or add the method to the prototype of any existing point object.

    Point.prototype.moveToOrigin = function () {this.x = 0; this.y = 0;}
    p.moveToOrigin();
    q.moveToOrigin();
    alert(p + " and " + q);

Hey! This looks like a better way to do methods. We don’t have to pollute the global namespace:

let Point = function (x, y) {
    this.x = x || 0;
    this.y = y || 0;
};
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 q = new Point(3, 7);
Important Exercise: We just saw 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?

Any object can have a prototype, but prototypes are most useful on constructor functions.

Inheritance

You can make deep "class hierarchies" once you are comfortable with prototype chains, but if you find yourself doing this frequently, or making particularly deep hierarchies, ask yourself why you are using JavaScript.

One way to wing this, using a classic (pun intended) example:

/*
 * Ugly deep hierarchy.
 */
function Person(name, birthday) {
    this.name = name || "Unknown"
    this.birthday = birthday;
}

Person.prototype.age = function () {
    return new Date().getTime() - this.birthday.getTime();
}

function Employee(name, birthday, id) {
    Person.apply(this, [name, birthday]);
    this.id = id;
}
Employee.prototype = new Person();

function Student(name, birthday, gpa) {
    Person.apply(this, [name, birthday]);
    this.gpa = gpa;
}
Student.prototype = new Person();

function Administrator(name, birthday, id) {
    Employee.apply(this, [name, birthday, id]);
}
Administrator.prototype = new Employee();

function Instructor(name, birthday, id, department) {
    Employee.apply(this, [name, birthday, id]);
    this.department = department;
}
Instructor.prototype = new Employee();

One problem with this approach is the prototypes lose their constructor property, which hurts you if you are trying to do something like Java’s getClass() with it. You have to fall back to using instanceof; Object.prototype.toString.call() won’t work for things you define. Or go ahead and hack the constructor property back in!! Why are we doing this again???

If you still want to do this kind of stuff, Douglas Crockford has a page showing several different ways to do it. He concludes that "Because objects in JavaScript are so flexible, you will want to think differently about class hierarchies. Deep hierarchies are inappropriate. Shallow hierarchies are efficient and expressive." Good advice. Use JavaScript’s dynamic features; use prototypal inheritance properly; don’t let your thinking get mired in the rigidity of static classes.

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.

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(regex)
Returns an array of substrings split by the regex separator.

Important:

Metaprogramming

Prototype access

> let basicCircle = {x:0, y:0, r:1}
> let c = Object.create(basicCircle)
> c.x = 2
2
> c.color = 'blue'
'blue'
> Object.getPrototypeOf(c)
{ x: 0, y: 0, r: 1 }
> Object.getPrototypeOf(basicCircle)
{}
> Object.prototype.isPrototypeOf(basicCircle)
true
> basicCircle.isPrototypeOf(c)
true

What properties does an object have?

> 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

Introducing Property Descriptors

> Object.getOwnPropertyDescriptor(c, 'x')
{ value: 2,
  writable: true,
  enumerable: true,
  configurable: true }
> c.x = 10
10
> c.x
10
> Object.defineProperty(c, 'x', {writable: false})
{ x: 10, color: 'blue' }
> Object.getOwnPropertyDescriptor(c, 'x')
{ value: 10,
  writable: false,
  enumerable: true,
  configurable: true }
> c.x = 500
500
> c.x
10

The attributes depend on how the object is created

> a = {x: 1, y: 2}
{ 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 }

What kind of attributes can we attach to properties?

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.

Preventing extensions, sealing, freezing

How can I get a handle on all of the objects in my system?

You can get the native objects, at least, as long as you can get a handle on the global object. Global variables, remember, are properties of the global object, and you know how to get properties of an arbitrary object.

Lexical Matters

JavaScript

Reserved Words

Reserved words currently being used

break      else       instanceof   true
case       false      new          try
catch      finally    null         typeof
continue   for        return       var
default    function   switch       void
delete     if         this         while
do         in         throw        with

Reserved words without any use today

abstract   enum         int         short
boolean    export       interface   static
byte       extends      long        super
char       final        native      synchronized
class      float        package     throws
const      goto         private     transient
debugger   implements   protected   volatile
double     import       public

Identifiers

Syntax of identifiers

String Literal Escapes

Escape SequenceCode Point (in Hex)Character Name
\b 8 Backspace
\t 9 Horizontal Tab
\n A Line Feed
\v B Vertical Tab
\f C Form Feed
\r D Carraige Return
\" 22 Double Quote
\' 27 Single Quote
\\ 5C Backslash
\xh1h2
(two hex digits)
10 * h1 + h2 -
\uh1h2h3h4
(four hex digits)
1000 * h1 + 100 * h2 + 10 * h3 + h4 -

Operators

From highest to lowest precedence

Operators Associativity Description
.
[]
L member (rhs cannot start with a digit)
member
()
new
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
eval and return undefined
delete
*
/
%
L multiply
divide
modulo
+
-
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 equals
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
, L comma

Good to know:

Built-in Objects

Here are the built-in objects of ECMAScript 5th edition, shown as a hierarchy, where each object’s parent is the value of its prototype. The prototype of Object.prototype is null.

Other Objects

Host environments provide lots of their own objects. You’re likely to see these in most web environments:

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

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.

There are two other ways to do graphics in JavaScript:

Most browsers have an additional object called XMLHttpRequest. This allows communication with a webserver without a page refresh, a programming style now called Ajax.

Server side environments provide a number of global objects, too. Here are some global objects in node:

console    setTimeout       Buffer         Int8Array
process    clearTimeout     ArrayBuffer    Uint8Array
module     setInterval                     Int16Array
require    clearInterval                   Uint16Array
global     setImmediate                    Int32Array
root       clearImmediate                  Uint32Array
                                           Float32Array
                                           Float64Array

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!

Probably the major JavaScript libraries are:

You might like A Commentary on Google Closure Library.

On the server side, there are thousands and thousands of packages you can use in your code. Run

$ npm search

Versions of JavaScript

Most browsers as of 2010 include a browser engine based on ECMAScript 3rd edition (ES3), which was standardized in 1999. In 2009, ECMAScript 5th edition (ES5) was standardized. Many browsers implement some, but not all of the ES5 features. ES6 is currently under development and scheduled for release in 2013. Many ES6 features are already in Firefox and Chrome.

Good History of JavaScript Links:

Good ES5 Links:

Future of JavaScript:

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

Wat

JavaScript is awesome, but sometimes you just gotta have fun. Watch Gary Bernhardt’s famous Wat talk.