In a nutshell:
- Created by Brendan Eich at Netscape, in 1995.
- The name comes from a cooperation with Sun Microsystems but it has nothing to do with Java.
- Standardized by ECMA (ECMAScript Language Specification).
- Interpreted language.
- Dynamic typing.
- Object Oriented language based on Prototypes.
- Functional language with nested functions and closures.
Below is a detailed overview of the basis of the language. This presentation is heavily based on JavaScript: The Good Parts by Douglas Crockford. This more or less correspond to the fift version of the ECMAScript standard (ES5). The sixth version ES6 or ES2015 has many new features (classes, modules, arrow functions, etc.) that are not described here.
JavaSript has a c-based syntax with lots of idioms borrowed from Java.
Variables are dynamically typed but need to be declared with the var
keyword:
var i;
var j = 0;
var k = 1, l = j+k;
/*
C-Style multiline comment
*/
// C-Style single line comment
One letter or underscore optionally followed by one or more letters, digits, underscores.
Most of them are not used.
abstract boolean break byte case catch char class const continue debugger default delete do double else enum export extends false final finally float for function goto if implements import in instanceof int interface long native new null package private protected public return short static super switch synchronized this throw throws transient true try typeof var volatile void while with
Number
is the single type for all numerical values. No integers, float, double, etc. Only numbers. A number is composed of :
- an integer part, e.g.
123
, - followed by an optional fraction part, e.g.
123.456
- followed by an optional exponent part, e.g.
123.456e+7
- 64 bits floats (Java doubles)
- No integers, 1 is the same value as 1.0.
- Arithmetic is not exact, ( 0.1 + 0.2 !== 0.3) as in all programming languages...
NaN
is a value that results from an operation producing abnormal arithmetic result.
NaN
can't be tested against itself:
Math.sqrt(-2) === NaN; // false
isNaN(number)
is used to spot NaNs.
isNaN(Math.sqrt(-2)); // true
Infinity > 1.79769313486231570e+308 // true
Utility functions and constants are available through the Math object.
Math.floor(3.45); // 3
Math.random(); // 0.22312605078332126
Math.PI; // 3.141592653589793
Math.sin(Math.PI/2); // 1
parseInt(string, base);
parseInt('345€', 10); // 345
parseInt('$345', 10); // 'undefined'
parseInt('8'); // 8
parseInt('08'); // 0 -> leading 0 is understood as octal base
parseInt('08', 10); // 8 -> always give the base!!!
Strings are immutable objects.
Written between single or double quotes ( 'this string', "that string").
- The empty string '' is allowed (0 characters).
- No char type. We use one-character strings ( 'a').
\
(backslash) escapes characters.\\ \' \' \n \/ \t \b \f \r \u0065
- Strings are 16 bits unicode characters.
'\u004A \u0053 \u062D \u0F1C \u3FEF \u0DF4' === 'J S ح ༜ 㿯 ෴'
- Characters above '\uFFFF' need 2 JS characters.
The +
operator has 2 functions:
- concatenation of strings
- and addition of numbers.
'J' + 'S' === 'JS' // true
The concatenation has the priority over the addition. Addition will occure only if the two operators are numbers. In any other case, concatenation will occure.
'HTML' + 10 / 2 === 'HTML5' // true
String objects have a length
property that represents the number of 16-bits unicode characters in that string.
'€\u5555ñ'.length === 3 // true
'€\u5555ñ' === '€啕ñ' // true
The String pseudo-class has methods.
Since strings are immutable, methods are static and only return new objects (no modification).
var s = 'ok/ko';
var s1 = s;
s += '/ok'; // 'ok/ko/ok' but strings are immutable, so s is a new object.
s1; // 'ok/ko' The original object remains unchanged.
s1.toUpperCase(); // 'OK/KO'
s1.split('/'); // [ 'ok', 'ko' ]
s1.replace('/', ' ≠ '); // 'ok ≠ ko'
'one, two , three'.split(/\s*,\s*/); // [ 'one', 'two', 'three' ]
String.fromCharCode(74, 83) // 'JS'
Classical C-Style Block statements.
if (expression) {
// statements;
} else if (expression) {
// statements;
}
var i; // at beginning of function
// ...
for (i = 0; i < 10; i += 1) {
// statements;
}
for (i in obj) {
// statements;
}
var i; // at beginning of function
// ...
i = 0;
while (i < 10) {
// statements;
}
switch (expression) {
case expression:
// statements;
break;
default:
// statements;
}
Literals, names, operators and other expressions.
Operator | comment |
---|---|
. [] () |
Refinement and invocation |
delete new typeof - + ! |
Unary operators |
* / % |
Multiplication, division, modulo |
+ - |
Addition (or string concatenation), subtraction |
<= < >= > |
Inequalities |
=== !== |
Equality |
&& |
Logical AND |
` | |
?: |
Ternary operator |
object.property;
object['property'];
my_function(param1, param2);
{
property1: 'value1',
my_property: true,
'% of value': 23
}
['a', 'b', 'c', 3, true, my_obj]
/^[a-zA-Z_][a-zA-Z_0-9]*$/ // recognizes javascript 'names'
function my_function(p1, p2) {
// var statements;
// statements;
}
some_function(p1, p2, function(){
// var statements;
// statements;
});
Although being a block-based syntax language, JavaSript does not have block scope. Javascript has function scope.
Functions get access to the context (surrounding variables) they are defined within.
function f () {
var a = 10, b = 2;
function f2() {
var b = 20, c = 2;
// a : 10, b : 20, c : 2
}
f2();
// a : 10, b : 2, 'c' exists but is undefined
if (a > 0) {
var c = 34;
b = 25;
// a : 10, b : 25, c : 34
}
// a : 10, b : 25, c : 34
}
f();
A function scope extended example.
- Numbers, strings, booleans,
null
andundefined
are simple and immutable types. - All other values are objects.
- Objects are mutable dictionaries (Java's hashtables, Python's dicts).
- An object is a container for properties.
- A property has a name and a value. A name of a property can be any string (quotes are optional if name matches
/^[a-zA-Z_][a-zA-Z_0-9]*$/
). - A value of a property can be any JS value except for
undefined
.
var w = {
'°C': 27,
humidity: '80%',
place: 'Le Havre'
}
w['°C']; // 27
w.humidity; // '80%'
w.pressure; // undefined
w.pressure = 1030;
w.place = 'Nice';
var w2 = w;
w['°C']=34;
w2['°C']; // 34
-
Every object inherits properties from a prototype object.
-
Object literals are linked to
Object.prototype
. -
New objects can have any object as a prototype.
-
Accessing an object's property is a recursive search into the prototype chain.
-
Recursively, all objects inherit from
Object.prototype
. -
object
- properties
- prototype
- properties
- prototype
- ...
- ...
Example:
var o = {
'a': 0,
'b': false
};
Object.prototype.ok = function() {
return 'That\'s OK!';
};
Object.prototype.not_ok = 'Not OK.';
o.ok(); // 'That's OK!'
o.not_ok; // 'Not OK.'
Searching the prototype chain: - object o
- properties {'a':0, 'b':false}
- prototype Object.prototype
- properties { ok: [Function], not_ok: 'Not OK.'}
- prototype undefined
##Creation of new Objects
Several technics allow the creation of new objects. The most common uses Constructor Functions and the new
operator.
Like an ordinary function, with a capital first Letter name (convention) The function body can be described as the constructor of the new object. The new object's properties can be accessed/defined in the constructor with the this
special value.
function Point() {
this.x = 0;
this.y = 0;
this.toString = function() {
return '('+ this.x + ', ' + this.y + ')';
};
}
The new
creates new objects using a Constructor Function.
It gives newly created objects properties referred to by this
in the body of the Constructor Function.
var p1 = new Point();
p1; // { x: 0, y: 0, toString: [Function] }
new
operator uses the Constructor Function's prototype
property to initialise the new object's prototype.
A Constructor Function is an object like an other JS object. It has properties an a prototype.
Object | Point |
---|---|
Properties | prototype: {} |
Prototype | Function.prototype |
Object | p1 |
---|---|
Properties | x: 0, y: 0, toString: [Function] |
Prototype | Point.prototype |
- The special class method
Object.getPrototypeOf(object)
retrieves an object's prototype.
Object.getPrototypeOf(p1) === Point.prototype
```
- Changing an object's properties does not change its prototype object.
```javascript
p1.z = 0;
Point.prototype.z; // undefined
```
- :warning: Changing an object's prototype will change all the objects that use this prototype!
```javascript
var p2 = new Point();
p2.z; // undefined
var proto = Object.getPrototypeOf(p1); // {}
proto.z = -1;
p1.z; // -1
p2.z; // -1
Point.prototype; // { z: -1 }
```
### Dealing with properties of an object
The `for in` statement iterates through the properties of an object.
```javascript
var prop;
for (prop in p1){
console.log(prop+' : '+p1[prop]);
}
The for in loop
might bring properties inherited from the prototype chain. hasOwnProperty
filters only properties (not function) belonging to that object.
for(var prop in options) {
if(options.hasOwnProperty(prop) && this.hasOwnProperty(prop)) {
this[prop] = options[prop];
}
}
Delete a property from an object. Cannot affect properties from the prototype.
delete p1.x; // true
p1.x; // undefined
Preexisting objects also have a prototype link that can be accessed.
The String pseudo-class
can be augmented. We call it a pseudo-class because it does not create real mutable objects. Indeed strings are immutable.
String.prototype.trim = function() {
return this.replace(/^\s*|\s*$/, '');
};
' Ok then '.trim(); // 'Ok then'
Number.prototype.integer = function() {
return Math[this >= 0 ? 'floor' : 'ceil'](this);
};
(2.34).integer(); // 2
(-Math.PI).integer(); // -3
The ECMASript 5 uses the Object.create
function to create objects.
Object.create(proto[, propertiesObject])
This Method returns a new object based on the given prototype and optionally the given properties.
The proto
parameter can be null
or Object.prototype
or any other object. Newly created objects will have this proto
object as a prototype link.
The propertiesObject
parameter defines the new objects properties.
The added value of this approach is the possibility to tune properties with a set of parameters that define their behavior.
configurable
: iftrue
, then it is possible to change this property's type.false
by default.enumerable
: if true, then this property can by eniumerated (e.g.for(var prop in obj){...}
).false
by default.value
: The value associated with that property. Can be any Javascript type (Number, String, object, function, etc.)undefined
by default.writable
: iftrue
, then the property can be modifiable. It is then possible to set a value without using accessors.false
by default.get
: An accessor function. This function returns what is considered to be the value of the underlying property.undefined
by default.set
: A modifier function that sets a value to the property. This function has one parameter: the new value to be given to the property.undefined
by default.
The most important feature of Object.create
is to provide a way to define safe objects. Values car be read only or protected with accessors... But attributes can't be private.
Example:
var obj = Object.create(null); // object without prototype link
var obj2 = Object.create(Object.prototype); // equivalent to "var obj2 = {};"
var positivePoint = Object.create(Object.prototype, {
x: {
get: function() {return this._x || 0;},
set: function(value) {
if (value < 0) {
console.log("Error! this point must have positive values", this.y);
}
else {
this._x = value;
}
},
},
y : {
get: function() {return this._y || 0;},
set: function(value) {
if (value < 0){
console.log("Error! this point must have positive values");
}
else {
this._y = value;
}
},
},
toString: {
value: function() {
return '('+ this.x + ', ' + this.y + ')';
},
}
});
- Literals
var a = [ 'a', 'b', ['c', 'd'], true]; var b = [];
- Access with integer indices
a[0]; // 'a'.
- Javascript's Arrays are ordinary objects (key/value) with arrays characteristics.
- Indices are converted into keys (strings)
a[0] === a['0'] // true.
- Arrays can be sparse (no memory wasted):
a[10] = 'k'; a[8]; // 'undefined' a; // ['a', 'b', ['c', 'd'], true, , , , , , , , 'k'] var prop; for (prop in a) { if (a.hasOwnProperty(prop)) { console.log(prop);} } // 0 1 2 3 10
Arrays have a length property. The highest index plus one, not this actual size:
a.length; // 11
a[a.length]='v1';
a.push('v2');
The delete operator can be use (as for objects) but array will not be rearranged. We use splice instead.
delete a[2]; // ['a', 'b', , true, , , , , , , , 'k', 'v1', 'v2']
a.length; // 13
a.splice(2, 1); // ['a', 'b', true, , , , , , , , 'k', 'v1', 'v2']
a.length; // 12
Returns a new array that has the items of this plus the flatten items given in parameters:
var a = ['a', 'b', 'c'];
var b = ['d', 'e', 'f'];
var c = a.concat(b, 'g', 'h');
c; // ['a','b','c','d','e','f','g','h']
Appends items in parameters to this array.
a.push(b, 'g', 'h');
a; // ['a', 'b', 'c', ['d', 'e', 'f'], 'g', 'h']
Returns a concatenated string of values of the array. The separator is inserted between each value.
a.join('-'); // 'a-b-c-d-e-f-g-h'
Removes and returns the last element of the array. Arrays can be used as stacks when pop() and push(...) are used.
var a = ['x', 'y', 'z'];
a.pop(); // 'z', a : ['x', 'y']
Adds items at the beginning of the array. Returns the size of the array. Existing elements are shifted.
a.unshift('a'); // 3
a; // ['a', 'x', 'y']
Removes and returns the item at the beginning of the array.
a.shift(); // 'a'
a; // ['x', 'y']
Reverses the order of the array and returns it.
Removes 'len' elements at position 'beg' and insert items 'item...' at that position. Re-indexing if needed.
var a = ['a', 'b', 'c'];
a.splice(1, 1, 'b1', 'b2'); // ['b']
a; // ['a', 'b1', 'b2', 'c']
Return a copy array (references only if objects) of elements between beg and end indices. Original array not affected.
a.slice(1, 2); // ['b1', 'b2'];
a; // ['a', 'b1', 'b2', 'c']
Reorders the array according to the given function. The function takes 2 args ( a,b) and returns 0 id a === b, a positive number if a > b, and a negative number if b > a.
function by(prop){
return function(obj1, obj2){
var a = obj1[prop],
b = obj2[prop];
if(a === b){
return 0;
}
return a > b ? 1 : -1;
}
}
var pts = [ {x:10,y:34},
{x:-10,y:4},
{x:0,y:-4}];
var i;
pts.sort(by('y'));
for(i = 0 ; i < pts.length; i++) {
console.log(Point.prototype.toString.apply(pts[i]));
}
// (0, -4)
// (-10, 4)
// (10, 34)
- from ECMAScript 5.1
New array with the result of the execution of callback
on each value of that array.
var a = [1, 2, 3, 4];
var map_a = a.map(function(e){
return e*e;
});
// [1, 4, 9, 16]
- from ECMAScript 5.1
Apply the callback
function between an accumulator and each value of the array, from left to right.
var reduce_a = map_a.reduce(function(previous, current){
return previous + current;
});
// 30
- For search and replace operation in strings.
- Faster than equivalent string operations.
- Not as powerful as real Perl regex.
- Main methods:
exec
,test
,match
,replace
,search
,split
.
/* matching ip addresses */
var worst = /^\d+\.\d+.\d+.\d+$/;
var bad = /^(?:\d{1,3}\.){3}\d{1,3}$/
var better = /^(?:\b(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b\.){3}
\b(?:25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\b$/
Full example on CodePen.
More on Mozilla Developer Network's article on RegExp.