Updated on
JavaScript Basics (part 1)
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 was first based on JavaScript: The Good Parts by Douglas Crockford. This more or less correspond to the fifth version of the ECMAScript standard (ES5).
The sixth version ES6 or ES2015 has many new features (classes, modules, arrow functions, etc.) that are also presented. New features are usually preceded with the or emoji.
- Syntax
- Numbers
- Strings
- Control Flow
- Expressions
- Literals
- Scope
- Objects
- Prototype
- Creation of new Objects
- Arrays
- Regular Expressions
Syntax
JavaSript has a c-based syntax with lots of idioms borrowed from Java.
Variables
Variables are dynamically typed but need to be declared with the var
keyword:
var i;
var j = 0;
var k = 1, l = j+k;
Par défaut la portée des variables s’étend à toute la fonction dans laquelle elles sont créées. Ceci est différents des langages classiques (C, Java) où la porté des variables est limitée au bloc dans lequel elles sont déclarées.
le mot clé let
ES6 introduit un nouveaux mot clés let
pour déclarer des variables avec une portée limitée au bloc courant. let
devient le nouveau mot clé par défaut pour déclarer une variable.
le mot clé const
ES6 introduit un nouveaux mot clés const
pour déclarer des constantes. Sa portée est limitée au bloc. A utiliser dès que possible quand on sait que les variables ne seront pas réassignées.
variable réassignée ≠ objet modifié
let a = 0;
a = 1; // a est réassigné
const b = 0;
b = 1; // TypeError: Assignment to constant variable.
const o = new SomeObjet();
o.someMethod(); // pas de réassignation, pas d'erreur.
Comments
/*
C-Style multi line comment
*/
// C-Style single line comment
Names
One letter or underscore optionally followed by one or more letters, digits, underscores.
Reserved Words
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
Numbers
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
Numbers Encoding Format
- 64 bits floats (Java doubles)
- No integers, 1 is the same value as 1.0.
- Arithmetic is not exact… as in all programming languages…
0.1 + 0.2 === 0.3 // false
On peut vérifier l’égalité de 2 nombres réels avec la constante Number.EPSILON
qui représente la marge d’erreur “raisonnable” entre deux nombres.
Math.abs((0.1 + 0.2) - 0.3) < Number.EPSILON //true
NaN
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
in ES5 isNaN(number)
is used to spot NaNs.
isNaN(Math.sqrt(-2)); // true
Math.sqrt(-2).isNaN();
in ES6 Number.isNaN(number)
is a more robust solution.
Infinity
Infinity > 1.79769313486231570e+308 // true
Vérifier si un nombre est bien un “vrai” nombre :
Number.isFinite(Infinity) // false
Number.isFinite(-Infinity) // false
Number.isFinite(NaN) // false
Number.isFinite(1234) // true
Vérifier si un entier peut être encoder sans perte avec Number.isSafeInteger(number)
> Number.isSafeInteger(1e+123)
false
> Number.isSafeInteger(1e+9)
true
> Number.MAX_SAFE_INTEGER
9007199254740991
Literals
In ES5 decimal and hexadecimal literals are supported
let elevenInDecimal = 11; // 11
let elevenInHexadecimal = 0x11; // 18
elevenInHexadecimal = 0xb; // 11
ES6 supports octal as well as binary literals.
let elevenInBinary = 0b11; // 3
let elevenInOctal = 0o11; // 9
Utility functions
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
Parse Strings to Numbers
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
Strings are immutable objects.
String Literals
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'
).
Escaped Characters
-
\
(backslash) escapes characters. \\ \' \' \n \/ \t \b \f \r \u0065
Unicode
- Strings are 16 bits unicode characters.
'\u004A \u0053 \u062D \u0F1C \u3FEF \u0DF4' === 'J S ح ༜ 㿯 ෴'; // true
- Characters above ‘\uFFFF’ need 2 JS characters.
Concatenation
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
Length
String objects have a length
property that represents the number of 16-bits unicode characters in that string.
'€\u5555ñ'.length === 3 // true
'€\u5555ñ' === '€啕ñ' // true
String Methods
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'
Control Flow
Classical C-Style Block statements.
if/else
if (expression) {
// statements;
} else if (expression) {
// statements;
}
for
var i; // at beginning of function
// ...
for (i = 0; i < 10; i += 1) {
// statements;
}
for (i in obj) {
// statements;
}
La boucle for...in
itère sur les noms des propriétés d’un objet. Certains objets peuvent avoir des propriétés que l’on ignorait. A éviter.
Nouvelle boucle for...of
itère sur les données de l’objets itérables (String
, Array
, Map
, Set
, TypedArray
, l’objet arguments
, les fonctions génératrices).
for (let chr of "😺😲") {
console.log(chr);
}
for (let chr of ["😺","😲"]) {
console.log(chr);
}
while
var i; // at beginning of function
// ...
i = 0;
while (i < 10) {
// statements;
}
switch
switch (expression) {
case expression:
// statements;
break;
default:
// statements;
}
Expressions
Literals, names, operators and other expressions.
Operators Precedence
Operator | comment |
---|---|
. [] () |
Refinement and invocation |
delete new typeof - + ! |
Unary operators |
* / % |
Multiplication, division, modulo |
+ - |
Addition (or string concatenation), subtraction |
<= < >= > |
Inequalities |
=== !== |
Equality |
&& |
Logical AND |
|| |
Logical OR |
?: |
Ternary operator |
Exponentiation
var1 ** var2
Raise the first parameter to the power of the second (like var1var2).
Exponentiation assignment
x **= y
Refinement
object.property;
object['property'];
Invocation
my_function(param1, param2);
Literals
Literal Objects
{
property1: 'value1',
myProperty_2: true,
'% of value': 23
}
Literal Arrays
['a', 'b', 'c', 3, true, my_obj]
Literal Regexp (borrowed from perl)
/^[a-zA-Z_][a-zA-Z_0-9]*$/ // recognizes javascript 'names'
Functions
function my_function(p1, p2) {
// var statements;
// statements;
}
Anonymous functions
some_function(p1, p2, function(){
// var statements;
// statements;
});
Arrow Functions
([param] [, param]) => {
instructions
}
une syntaxe plus courte pour une fonction anonyme. Les fonctions flèches n’ont pas de propriétés this
, arguments
et super
.
Scope
Although being a block-based syntax language, JavaSript does not have block scope (by default). 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.
Objects
- 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
.
Object Literal
var w = {
'°C': 27,
humidity: '80%',
place: 'Le Havre'
}
Access to properties
w['°C']; // 27
w.humidity; // '80%'
w.pressure; // undefined
w.pressure = 1030;
w.place = 'Nice';
Objects are passed by reference
var w2 = w;
w['°C']=34;
w2['°C']; // 34
Prototype
- 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
- properties
- properties
Creation of new Objects
Several technics allow the creation of new objects. The most common uses Constructor Functions and the new
operator.
Constructor Functions
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
operator
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] }
The 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 and 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; // true
- Changing an object’s properties does not change its prototype object.
p1.z = 0;
Point.prototype.z; // undefined
Changing an object’s prototype will change all the objects that use this prototype!
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.
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
Augmenting Default Types
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
Creating Objects with the ECMASript 5 pattern
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 enumerated (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 can be read only or protected with accessors… Attributes can’t really be private be they can be hidden.
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 + ')';
},
}
});
Création d’objets avec le mot clé class
les classes sont du “sucre syntaxique” au dessus du mécanisme de prototype. Ce n’est pas un nouveau mécanisme de création ou d’héritage. Seule la syntaxe est différente.
class Point {
constructor(x, y) {
this._x = x;
this._y = y;
}
get x() {
return this._x || 0;
}
get y() {
return this._y || 0;
}
static distance(a, b) {
const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.sqrt(dx*dx + dy*dy);
}
}
const p1 = new Point(5, 5);
const p2 = new Point(10, 10);
console.log(Point.distance(p1, p2));
Arrays
- 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
for (let v of a) {
console.log(v);
}
// a
// b
// [ 'c', 'd' ]
// true
// undefined
// undefined
// undefined
// undefined
// undefined
// undefined
// k
Length
Arrays have a length property. The highest index plus one, not this actual size:
a.length; // 11
Add elements
a[a.length]='v1';
a.push('v2');
Delete elements
The delete operator can be use (as for objects) but array will not be rearranged. We use splice
instead.
a.length; // 13
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
Arrays Methods
concat(item...)
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']
push(item...)
Appends items in parameters to this array.
a.push(b, 'g', 'h');
a; // ['a', 'b', 'c', ['d', 'e', 'f'], 'g', 'h']
join(separator)
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'
pop()
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']
unshift(item...)
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']
shift()
Removes and returns the item at the beginning of the array.
a.shift(); // 'a'
a; // ['x', 'y']
reverse()
Reverses the order of the array and returns it.
splice(beg, len, item...)
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']
slice(beg, end)
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']
sort(func)
Reorders the array according to the given function. The function takes 2 args (a,b)
and returns 0
if 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)
map(callback[, thisArg])
- 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( e => {
return e*e;
});
// [1, 4, 9, 16]
// a.map( e => e*e );
reduce(callback[, initialValue])
- 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((previous, current) => (previous + current));
// 30
Affectation par décomposition
L’affectation par décomposition (destructuring) permet d’extraire les données d’un tableau ou d’un objet.
let toto= ['un', 'deux', 'trois'];
let [un, deux, trois] = toto;
un + ', ' + deux; // 'un, deux'
let tab = [ ...toto, 4, 5];
tab; // [ 'un', 'deux', 'trois', 4, 5 ]
let [nope1, nope2, ...rest] = tab
rest; // [ 'trois', 4, 5 ]
échange de variable
[a, b] = [b, a];
Renvoyer plusieurs valeurs
function f() {
return [1, 2];
}
var a, b;
[a, b] = f();
Regular Expressions
- 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.