JavaScript Basics (part 1)

In a nutshell:

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 :six: or :new: emoji.

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;

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

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

:six: 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.

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

Numbers Encoding Format

0.1 + 0.2 === 0.3 // false

:six: 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.

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

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

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

:six: 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”).

Escaped Characters

Unicode

Concatenation

The + operator has 2 functions:

'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. :warning: A éviter.

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

:new: 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;
});

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

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

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:

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] }

:warning: 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
Object.getPrototypeOf(p1) === Point.prototype; // true
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.

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 + ')';
    },
  }
});

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

var a = [ 'a', 'b', ['c', 'd'], true];
var b = [];
a[0]; // 'a'.
a[0] === a['0'] // true.
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])

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

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

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

 /* 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.