JavaScript, the language, is
Bored already? Some Links:
Here's your first Javascript program (a one-liner, of course):
document.write("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 write of
the object that is the value of
the property named document of
the global object.
Note we didn't have to declare the global object, or its fields and methods.
The most common way to run Javascript programs is to embed them in XHTML documents. Load this XHTML file into a browser:
<html>
<head>
<title>A Greeting</title>
</head>
<body>
<script type="text/javascript">
document.write("Hello, World.");
</script>
</body>
</html>
Where did this document object come from? And for that matter, what about those windows, menus, pop-ups, dialog boxes, textareas, anchors, frames, histories, cookies, that you may have seen in other JavaScript code? They come from the host environment. In this case it was a web browser, but it doesn't have to be.
A more complicated program:
// A script with two functions and code to exercise them
function today() {
return new Date();
}
function cubed(x) {
return x * x * x;
}
var n = 10;
document.write("<b>Some cubes for " + today() + "</b>");
for (i = 0; i < n; i++) {
document.write("<br />" + cubed(i));
}
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.
The ECMAScript language standard defines a core set of types, values, objects, properties and functions. A conforming implementation may add extra types, values, objects, properties and functions. JavaScript 1.5 conforms to ECMA-262, edition 3.
If you come from the C, C++, Java, Ada world where compilers seem to check everything, remember Javascript is a dynamic language like Perl, Python, PHP, PostScript, Smalltalk, Self, etc:
There are 6 types
| Type | Values 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:
|
| String | Quote-delimited strings of zero or more UTF-16 code units. Examples:
|
| Object | Everything that isn't one of the above five types. |
JavaScript is pretty loose with types, when expressions don't have the expected type JavaScript will often compute a derived expression that does.
Complete details are in Section 9 of the ECMAScript Standard.
A program is a sequence of statements and function declarations. The kinds of statements are:
Objects have properties. Access the properties with either dot or square bracket notation. Use curly braces to define the object all at once. Note: You don't make a class and declare the properties; you just start using them:
var x = new Object();
x.age = 17;
x.height = 65.3;
var y = x.age * x["height"];
var z = {age: 30, color: "red", total: 3};
var cool = {triple: {a:4, b:undefined, c:{4:null}}, 7: "stuff"};
An array is a kind of object with "numeric properties." Fortunately JavaScript provides a convenient syntax for them, too:
a[0] = 5 * a[i];
var y = a.length;
var a = new Array();
var b = new Array(10); // initialized with 10 cells
var c = new Array(1, 5, 25); // initialized with 3 cells
var d = [1, 5, 25];
var e = [1, true, [1,2], {x:5, y:6}, "Hi"];
var f = {triple: {a:4, b:"dog", c:[1,null]}, 7: "stuff"};
var g = [f.triple.a, f[7]];
var a = new Array(3); a[0] = 1; a[1] = "hello"; a[2] = a;
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 latter two make it clear that a function is just an object. The third one, though very cool, is very slow (why?)
Because a function is an object,
Examples:
function plusSix (x) {return x + 6;}
function squared (x) {return x * x;}
function twice (f, x) {return f(f(x));}
document.write(twice(plusSix, 5) + " ");
document.write(twice(squared, 4) + " ");
function compose (f, g) {return function(x) {return g(f(x));}}
var squareThenPlusSix = compose(squared, plusSix);
document.write(squareThenPlusSix(10) + " ");
document.write(compose(plusSix, squared)(5) + " ");
document.write("twice expects " + twice.length + " arguments");
If you call a function that is a property of an object, within that function the expression this refers to the containing object.
var x = {a:1, b:function(x) {return x + this.a;}};
document.write(x.b(2));
will write 3.
The expression this is evaluated dynamically, not statically:
function f(x) {return x + this.a;}
var x = {a:10, b:f};
document.write(x.b(2));
will write 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();
document.write(p.x + " " + p.y);
This made something that looks like a point object. What if we want a whole bunch of points?
Check this out:
function Point() {
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();
document.write(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:
function point_move(dx, dy) {this.x += dx; this.y += dy;}
function point_reflect() {this.x = -this.x; this.y = -this.y;}
function point_to_string() {return "(" + this.x + "," + this.y + ")";}
function Point(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)
document.write(p + " " + q);
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
document.write(p + " " + 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();
document.write(p + " " + q);
Hey! This looks like a better way to do methods. We don't have to pollute the global namespace:
function Point(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);
This works because If you try to access (read the value of or call), say, x.p and x doesn't have property p, JavaScript will (recursively) look for p in x.__proto__. (And remember the value of x.__proto__ is set to the value of y.prototype where y is the constructor that constructed x.)
Any object can have a prototype, but prototypes are most useful on constructor functions.
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:
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; don't let your thinking get mired in the rigidity of static classes.
JavaScript
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
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
Syntax of identifiers
| Escape Sequence | Code 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 | - |
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:
Entities in bold are specific to JavaScript 1.5 and not ECMA-262.
| Name | Properties | __proto__ | Notes |
|---|---|---|---|
| Global | NaN Infinity undefined Math eval() parseInt() parseFloat() isNaN() isFinite() decodeURI() decodeURIComponent() encodeURI() encodeURIComponent() Object() Function() Array() String() Boolean() Number() Date() RegExp() Error() EvalError() RangeError() ReferenceError() SyntaxError() TypeError() URIError() | [implementation dependent] | Not directly accessible |
| Object | length prototype | Function.prototype | |
| Object.prototype | constructor toString() toLocaleString() valueOf() hasOwnProperty() isPrototypeOf() propertyIsEnumerable() toSource() watch() unwatch() | null | |
| Function | length prototype | Function.prototype | instance properties: length prototype |
| Function.prototype | constructor |
Object.prototype | |
| Array | length prototype | Function.prototype | instance properties: length |
| Array.prototype | constructor index input concat() join() pop() push() reverse() shift() slice() splice() sort() unshift() every() some() filter() forEach() map() indexOf() lastIndexOf() | Object.prototype | |
| String | length prototype fromCharCode() | Function.prototype | instance properties: length |
| String.prototype | constructor toString() valueOf() charAt() charCodeAt() indexOf() lastIndexOf() localeCompare() match() concat() split() splice() slice() substring() replace() search() toLowerCase() toLocaleLowerCase() toUpperCase() toLocaleUpperCase() (plush a bunch of crap methods that write out crappy HTML — don't use that crap) | Object.prototype | |
| Boolean | length prototype | Function.prototype | |
| Boolean.prototype | constructor toString() valueOf() | Object.prototype | |
| Number | length prototype MAX_VALUE MIN_VALUE NaN NEGATIVE_INFINITY POSITIVE_INFINITY | Function.prototype | |
| Number.prototype | constructor toString() toLocaleString() valueOf() toFixed() toPrecision() toExponential() | Object.prototype | |
| Math | E PI SQRT_2 SQRT1_2 LN2 LN10 LOG2E LOG10E abs() sin() cos() tan() acos() asin() atan() atan2() exp() log() ceil() floor() min() max() pow() random() round() sqrt() | Object.prototype | |
| Date | length prototype parse() UTC() | Function.prototype | |
| Date.prototoype | constructor toString() toDateString() toTimeString()
toLocaleString() toLocaleDateString() toLocaleTimeString()
valueOf() getTime() getFullYear() getUTCFullYear() getMonth()
getUTCMonth() getDate() getUTCDate() getDay() getUTCDay()
getHours() getUTCHours() getMinutes() getUTCMinutes()
getSeconds() getUTCSeconds() getMilliseconds() getUTCMilliseconds()
getTimezoneOffset() setTime() setMilliseconds()
setUTCMilliseconds() setSeconds() setUTCSeconds()
setMinutes() setUTCMinutes() setHours() setUTCHours()
setDate() setUTCDate() setMonth() setUTCMonth()
setFullYear() setUTCFullYear() toUTCString()
|
Object.prototype | |
| RegExp | length prototype | Function.prototype | instance properties: source global ignoreCase multiline lastIndex |
| RegExp.prototype | constructor exec() test() toString() | Object.prototype | |
| Error | length prototype | Function.prototype | |
| Error.prototype | constructor name message toString() | Object.prototype | |
| EvalError | length prototype | Function.prototype | |
| EvalError.prototype | constructor name message() | Error.prototype | |
| RangeError | length prototype | Function.prototype | |
| RangeError.prototype | constructor name message() | Error.prototype | |
| ReferenceError | length prototype | Function.prototype | |
| ReferenceError.prototype | constructor name message() | Error.prototype | |
| SyntaxError | length prototype | Function.prototype | |
| SyntaxError.prototype | constructor name message() | Error.prototype | |
| TypeError | length prototype | Function.prototype | |
| TypeError.prototype | constructor name message() | Error.prototype | |
| URIError | length prototype | Function.prototype | |
| URIError.prototype | constructor name message() | Error.prototype |
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
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":
Important:
Each property has between zero and four attributes:
| Attribute | Description |
|---|---|
| ReadOnly | writes to the property will be ignored |
| DontEnum | property will be skipped in a for-in enumeration |
| DontDelete | deletion attempts will be ignored |
| Internal | not accessible in normal code |
A couple aspects of JavaScript aren't so easy to explain just by using the notion of objects with properties. Examples:
These things, and others like them, are described in terms of execution contexts in Section 10 of the ECMAScript standard.