javascript_logo.png

Introduction to JavaScript

JavaScript is probably the most popular programming language in the world. If you want to write code that runs in a web browser, there are few other choices.

Overview

JavaScript, the language, is

JavaScript began as "Mocha," 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.

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, or JSFiddle.
  3. Shell (REPL). Use the JavaScript Shell, or the console that comes with your web browser. Node.js has its own REPL; more on this later.
  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
    var report = function (celsius, fahrenheit) {
        document.getElementById("result").innerHTML =
            celsius + "\xb0C = " + fahrenheit + "\xb0F";
    };
    
    document.getElementById("f_to_c").onclick = function () {
        var f = document.getElementById("temperature").value;
        report((f - 32) / 1.8, f);
    };
    
    document.getElementById("c_to_f").onclick = function () {
        var 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 6 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 +.)
Object Everything that isn't one of the above five types.

The first five 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 are weakly typed: they do conversions. It is pretty much always better to use === and !== instead. These don't do any conversions at all. For x===y the values AND types of x and y must match.

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

Complete details are in Section 9 of the ECMAScript Standard.

Statements

A program is a sequence of statements and function declarations. The kinds of 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.

var x = {};
x.age = 17;
x.height = 65.3;
var score = x.age * x["height"];
var z = {age: 30, color: "red", total: 3};
z.last = true;
var 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 only set the prototype when the object is created. In newer JavaScript versions, you do this with Object.create:

var protoCircle = {x: 0, y: 0, radius: 1, color: "black"};
var 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:

var a = [];
var b = [1, 5, 25];
var c = new Array();        // Lame way to say []
var d = new Array(4, 5, 6); // Lame way to say [4, 5, 6]
var e = new Array(10);      // 10 cells, all undefined.
var f = a.length;
var g = [1, true, [1,2], {x:5, y:6}, "Hi"];
var h = {triple: {a:4, b:"dog", c:[1,null]}, 7: "stuff"};
var 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 var x=2; dogs.x?
Exercise: Experiment some more with arrays. Does var 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?
   var 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;
}

var sum = function (x,y) {return x + y;}

var predecessor = new Function("x", "return x - 1;");

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. The middle one is preferred.

Because a function is an object,

Examples:

var plusSix = function (x) {return x + 6;};
var squared = function (x) {return x * x;};
var twice = function (f, x) {return f(f(x));};
alert(twice(plusSix, 5));
alert(twice(squared, 4));
var compose = function (f, g) {return function (x) {return g(f(x));}}
var 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:

var nextFib = function () {
    var a = 0, b = 1;
    return function () {
        var result = b;
        b = a + b;
        a = result;
        return result;
    }
}();

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 function called via Function.apply or Function.call, the value for this was passed in.
  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 used in a function called normally, it refers to the object the function was called through (then the function is called a method).
var x = {a: 1, b: function (x) {return x + this.a;}};
alert(x.b(2));                                         // Alerts 3

The expression this is evaluated dynamically, not statically:

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

This is starting to look useful...

var 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:

var 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;
};

var 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
var point_move = function (dx, dy) {this.x += dx; this.y += dy;}
var point_reflect = function () {this.x = -this.x; this.y = -this.y;}
var point_to_string = function () {return "(" + this.x + "," + this.y + ")";}

var 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;
};

var p = new Point();
var 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:

var 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 + ")";}

var 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

> var basicCircle = {x:0, y:0, r:1}
> var 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' ]
> var a = []; for (var 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:

Wat

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