Javascript

http://get-focus.github.io/formation-js/#/

JS !== jQuery

Les bases

## Les variables - `const` Variable constante qui ne peut plus être modifiée - `var` portée globale ou locale a une fonction - `let` portée du block courant `{}` > A retenir on utilise `const` au maximum et `let` dans les autres cas

Les strings

						
'déjà' < 'demain'              // => false
'déjà'.localeCompare('demain') // => -1 ('déjà' avant 'demain')

'déjà !'.toLocaleUpperCase()      // => 'DÉJÀ !'
'ÇA POUTRE'.toLocaleLowerCase()   // => 'ça poutre'

'one,two,three'.split(',')        // => ['one', 'two', 'three']
'one,,two,three'.split(/\W+/)     // => ['one', 'two']

'hello'.substring(1)      // => 'ello'
'hello'.slice(1, -2)      // => 'el' -> [1;length-2[
						
					

Les dates

						
new Date                   //  maintenant !
new Date(y,m,d[,h,m,s,ms]) // Valeur découpée. (un peu lourd...)

date.getFullYear()  // JAMAIS
date.getMonth()     // NAZE Java-like qui démarre à… zéro (janvier). #ugly
date.getDay()       // PERDUUUU ! C'est le DOW (Day Of Week), 0 = dim., 6 = s
date.getDate()      // Le jour du mois.  Super logique.
date.getHours()
date.getMinutes()
date.getSeconds()
date.getMilliseconds() // "Milliseconds", un seul mot : une seule initiale.

// Heure locale du navigateur/système.  On a les mêmes en getUTCXxx()…
						
					

Les dates

						
// "J'ai bien lu la doc"
date.getTime()  // => ex. 1290852603042

// "#superSmart"
+date

// Et sinon…
new Date(1301733937452)

// Mais aussi (car souci pour partir de composantes UTC) :
Date.UTC(y, m, d, h, mn, s, ms) // => numérique

						
					
A retenir : Momentjs (on voit ça plus tard...)

Les objets

						
let obj = { first: 'David', last: 'Lopez', age: 77 };
obj.first  // => 'David'
obj['age'] // => 42

obj.first = 'pierr';
obj['first'] // => 'pierr'

obj[77] = 'roberto';
obj[77]   // => 'roberto'
obj['77'] // => 'roberto'
obj.77    // => SyntaxError
						
					

Les objets avec ES6

						
let defaults = { first: 'John', last: 'Doe', age: 42 };
let trainer  = { last: 'Smith', age: 35 };
trainer      = { ...defaults, ...trainer, age: 36 }
// => { first: 'John', last: 'Smith', age: 36 }
trainers['a'+ 12] = 'Gino';
trainers.a12;
// Gino
						
					
				

Les tableaux

						
let names = ['John', 'David', 'Rodrigo'];

names.length
// => 3. R/W : astuce pour réutiliser un tableau!

names[0]
// => 'John'

names[12] = 'Pierre';
names.length
// => 13

names[9]
// => undefined (comme 10 et 11): c'est appelé "sparse array"
						
					
						

Les tableaux

let data = [1, 2, 3]; // arr1.concat(arg…) -> arr2 [déroule sur 1 niveau, ni "shallow" ni "deep"] data.concat(4, 5, 6) // => [1, 2, 3, 4, 5, 6] data.concat([4, 5, 6]) // => [1, 2, 3, 4, 5, 6] data.concat(4, [5, 6]) // => [1, 2, 3, 4, 5, 6] data.concat([4, [5, 6]]) // => [1, 2, 3, 4, [5, 6]] -- 2 niveaux ! data // => [1, 2, 3] -- intact ! // arr.join([sep = ',']) -> String data.join() // => '1,2,3' data.join('') // => '123' -- Fréquent en construisant du HTML // arr1.slice(signedBegin[, signedEnd = length]) -> arr2 -- négatif ok partout ! data.slice(1) // => [2, 3] data.slice(1, 1) // => [] data.slice(1, 2) // => [2] data.slice(1, -1) // => [2] data.slice(-2) // => [2, 3] data.slice(-2, 2) // => [2] data.slice(-2, -1) // => [2]
						

Les tableaux en ES6

let arr1 = ['one', 'two'], arr2 = ['three', 'four']; arr1.push(...arr2) // => 4 arr1 // => ['one', 'two', 'three', 'four'] //le map arr1.map(element => console.log(element)) // for…in itère sur LES PROPRIÉTÉS ÉNUMÉRABLES de N’IMPORTE QUEL OBJET var arr = ['hello', 'world', , 'cool']; for (var k in arr){ console.log(k)}; // => 0, 1, 3 // for…of itère sur les éléments d'un objet itérable for (var k of arr){ console.log(k)}; // => hello, world, undefined, cool
						

Haloween pour les tableaux ...

// Toi aussi, déguise-toi en tableau ! var fakeArray = { 0: '!', 1: 'ça torche', 2: 'JavaScript', length: 3 }; fakeArray.join = [].join; fakeArray.reverse = [].reverse; fakeArray.reverse().join(' '); // => 'JavaScript ça torche !' // Ou alors : fakeArray.__proto__ = Array.prototype; fakeArray.reverse().join(' '); // => 'JavaScript ça torche !' // Méthodes « génériques » utilisables: hn // concat, join, pop, push, reverse, shift, // slice, sort, splice, toString, unshift.

Les boucles

						
const ARRAY = [1,2,3,4];
for(let i = 0, _l=ARRAY.length; i < _l; i++ ){console.log(ARRAY[i])}

ARRAY.forEach(function(element, idx){console.log(element, idx)});

ARRAY.map(function(element, idx){console.log(element, idx)});
						
					

Les fonctions

						
function maFonction() {
	return 'test';
}
maFonction() // test;
const a = () => 'test';
a(); //test
						
					

JS c'est logique ...

						
== ou === ?!

42 == '42'        // => true  -- Argh, ça sent le PHP, là…
null == undefined // => true  -- hmmm…
null == 0         // => false -- heureusement !
0 == undefined    // => false -- heureusement !
0 == false        // => true  -- Façon C…
1 == true         // => true  -- Façon C…
42 == true        // => false -- Watcha ?! (x == ToNumber(y), ES3 §11.9.3)
'0' == false      // => true  -- Woah !
'' == false       // => true  -- Yowza ! 8-O  On y reviendra…
NaN == NaN        // => false -- Bin oué, c’est le principe…
						
					

En fait si ...

						
// avec ===, fini de jouer : vérif valeur ET TYPE !

42 === '42'        // => false
null === undefined // => false
null === 0         // => false
0 === undefined    // => false
0 === false        // => false
'0' === false      // => false
NaN === NaN        // => false -- rien à faire !
						
					
On utilise toujours le ===

in et delete

						

let person = { name: 'Joe', langs: ['fr', 'en'] };

'name' in person        // => true
person.age = 35;
'age' in person         // => true
person.age = null;
'age' in person         // => true
delete person.age
'age' in person         // => false
person.age              // => undefined

0 in person.langs       // => true
'0' in person.langs     // => true
person.langs[3] = 'de';
2 in person.langs       // => false
3 in person.langs       // => true
delete person.langs[3]
person.langs            // => ['fr', 'en', undefined, undefined]
						
					

Falsy / Truthy

XML

					

  
    
    
    
  

					
				

JSON

					
{
    "menu": {
        "id": "file",
        "value": "File",
        "popup": {
            "menuitem": [
                { "value": "New", "onclick": "CreateNewDoc()" },
                { "value": "Open", "onclick": "OpenDoc()" },
                { "value": "Close", "onclick": "CloseDoc()" }
            ]
        }
    }
}
					
				

Oublions jQuery

					
//Avant
const monElement = $('[data-my-selector]');

//Après
const monElement = document.querySelector('[data-my-selector]');
					
				

90% de jQuery en 15 lignes

					
/* bling.js */

window.$ = document.querySelectorAll.bind(document);

Node.prototype.on = window.on = function (name, fn) {
  this.addEventListener(name, fn);
}

NodeList.prototype.__proto__ = Array.prototype;

NodeList.prototype.on = NodeList.prototype.addEventListener = function (name, fn) {
  this.forEach(function (elem, i) {
    elem.on(name, fn);
  });
}
				
Voir le gist de Paul Irish

Les outils

Node.js

  • JS côté serveur
  • Apporte plein d'outils sous forme de modules
  • npm (node package module)

Node.js

  • package.json
  • node_modules
  • npm install -g ma-dependance-globale
  • npm install -g rimraf
  • npm install --save ma-dependance

Babel

Webpack

Debug

Prototypes / Constructor

Classes ?

  • Avant ES6 pas de classe
  • JS est un langage prototypale
  • En JS tout est un objet
  • Une propriété est identifiée par une paire nom / valeur
  • Une propriété peut être une fonction

Constructeur

  • Fonctions servant à initialiser un nouvel objet. Le nom du constructeur est un peu comme « le nom de la classe »…
  • Toute fonction peut servir de constructeur : il suffit de l’appeler avec l’opérateur new.
  • Elle dispose alors d’une variable implicite this, qui représente la nouvelle « instance ».
  • L’objet créé référence son constructeur : constructor.

Constructeur

						
function Person(first, last) {
  this.first = first;
  this.last = last;
}

var joeLopezPerson = new Person('Joe', 'Lopez');
var davidLopezz = new Person('David', 'lopez');
joeLopezPerson.first // => 'Joe'
davidLopezz.first   // => 'David'

// Si on jouait aux cons ?!
function LopezPerson(first, last) {
  this.first = first;
  this.last = last;
  return { first: 'Anne', last: 'Pas Lopez' };
}
var oops = new LopezPerson('Henry', 'Lopez');
oops.first // => Anne
						
					

Prototypes

  • Tout constructeur a un prototype : un objet qui définit les propriétés (et donc méthodes) partagées par tous les objets que produit ce constructeur.
  • Le prototype est « vivant » : si on le triture après l’appel au constructeur, ça marche quand même !
  • Techniquement, y’a plein d’autres trucs dans un prototype (réf. au constructeur, gestion de propriétés…).
  • On verra, plutôt vous verrez...

Prototype

						
function Person(first, last) {
  this.first = first;
  this.last = last;
}

// On augmente l'existant…
Person.prototype.fullName = function fullName() {
  return this.first + ' ' + this.last;
}
Person.prototype.greet = function greet() {
  alert('Salut je m’appelle ' + this.first);
}

var john = new Person('John', 'Smith');
john.fullName() // => 'John Smith'
john.greet()    // 'Salut je m’appelle John'
						
					

Prototype (Don't)

						
function Person(first, last) {
  this.first = first;
  this.last = last;
  this.fullName = function fullName() {
    return this.first + ' ' + this.last;
  }
  this.greet = function greet() {
    alert('Salut je m’appelle ' + this.first);
  }
}
var john = new Person('John', 'Smith');
john.fullName() // => 'John Smith'
john.greet()    // 'Salut je m’appelle John'
						
					
Pas bon... on copie le truc dans chaque constructeur

Les classes ES6

						
class TodoItem extends Component {
  constructor(props, context) {
    super(props, context);
    this.state = {
      editing: false
    };
  }

  handleDoubleClick() {
    this.setState({ editing: true });
  }
  …
}
						
					
Attention il s'agit de sucre syntaxique pas d'une classe comme en Java ou .NET. Le JS reste un langage prototypale.

La chaîne d'appel d'une méthode

obj.prop ou obj['prop'] (c'est équivalent)
  1. On part de l’objet indexé (obj)
  2. Si on trouve prop dans ses own properties, on s’arrête là
  3. Sinon, on passe sur le prototype du niveau supérieur : celui du constructeur de l’objet en cours*
  4. On reprend à l’étape 2, sauf si on était déjà sur Object.prototype, auquel cas le lookup est fini, et échoue (undefined).
*conceptuellement, constructor.prototype ou __proto__

La preuve en live...

						
function Person(first, last) {
  this.first = first;
  this.last = last;
}
Person.prototype.fullName = function fullName() {
  return this.first + ' ' + this.last;
};
const davidLopez = new Person('David', 'Lopez');

davidLopez.first      // => 'David',         own property
davidLopez.fullName() // => 'David Lopez', Person.prototype
davidLopez.toString() // => '[object Object]', Object.prototype

Person.prototype.toString = function personToString() {
  return '#Person ' + this.fullName();
};

davidLopez.toString() // => "#Person David Lopez"
						
					

Closure

Closure simple

						
function publicFx() {
  let dateAppel = Date.now();
  return function() { console.log(dateAppel); };
}
let privilegedFx1 = publicFx();
// Attendre un bref instant
let privilegedFx2 = publicFx();

// privilegedFx(1,2) sont en fait les fonctions internes construites au
// sein de publicFx, qui grâce aux règles de portée "voient"
// dateAppel.  Elles sont *closed over* par publicFx, ce qui fait
// que les valeurs de dateAppel au moment où les fonctions ont été
// renvoyéees sont préservées
privilegedFx1(); // => affiche la dateAppel du moment de la création de la fonction privilegedFx1!
privilegedFx2(); // => affiche la dateAppel d'après !
						
					

Les modules sont une utilisation des closures


function yourModule(require, module, exports) {

  let widgets = {};
  let util = require('util');
  let Widget = require('widgets/base');

  function CoolWidget(elt) { … }
  util.inherits(CoolWidget, Widget);
  // …

  module.exports = Widget;
}
					
Dans node on ne voit pas la fonction enrobante...

Le binding

Le problème

						
var name = 'Mr X';
let obj = {
  name: 'Joe Lopez',
  greet: function greet(whom) {
	console.log(this) 
    console.log(this.name + ' salue ' + whom);
  },
  greetAll: function greetAll(first, second, last) {
	console.log(this)
    [first, second, last].forEach(this.greet);
  }
};
obj.greet("les lopezs de France");
// => 'Joe Lopez salut les lopezs de France !'
let fx = obj.greet;
fx("l’atelier") // => '"Mr X salue l’atelier"'
obj.greetAll('David', 'Joe', 'Guénolé'); // => 'Mr X salue David, Mr X salue Joe, Mr X salue undefined'
						
					

Comment faire ? La closure?

						
const obj = {
  // …
  greetAll: function greetAll(first, second, last) {
    var that = this;
    [first, second, last].forEach(function(name) {
      that.greet(name);
    });
  }
}
						
					

Comment faire ? ES6 style?

						
const obj = {
  // …
  greetAll(first, second, last) {
    [first, second, last].forEach(name => this.greet(name));
	//Ultra fat
  }
}
						
					

call et apply

						


//let fx = obj.greet;
fx.call(obj, 'les singes') // Joe Lopez salue les singes


let xy = { 0: 'Zero', 1: 'One', length: 2 };
Array.prototype.join.call(xy, '-') // 'Zero-One'


fx.apply(obj, ['']) // => 'Joe salue l’atelier'
Array.prototype.push.apply(xy, ['Two', 'Three', 'Four']) // => 5
xy // => { 0: 'Zero', 1: 'One', 2: 'Two', 3: 'Three', 4: 'Four', length: 5 }

JS = async

Callback

						
function delayedAlert() {
  window.setTimeout(slowAlert, 2000);
}

function slowAlert() {
  alert("That was really slow!");
}
					

Les promesses

Une promesse a un état (pending, fullfilled, rejected).
Elle est asynchrone, et se termine soit par un succès soit par une erreur et renvoie une nouvelle promesse.

promise.then(successCb,errorCb).then(otherSuccess, otherCb).catch(errorHandlingFn)

Les promesses


const myPromise = new Promise((resolve, reject) => {
	ajaxCall({success: resolve, error: reject});
})

Promise.resolve([1,2,3,4]);

Promise.reject('ma super erreur')

					

Les promesses


fetch('/users.json')
  .then(function(response) {
    return response.json()
  }).then(function(json) {
    console.log('parsed json', json)
  }).catch(function(ex) {
    console.log('parsing failed', ex)
  })					

ES6 / 2015

Les modules de node

						
//Dans un fichier
module.exports = monObjetAExporter;

//Utilisation
require('./mon_module_locale');
//Utilisation d'un module npm
require('mon_module');



					

Les modules de Papa

						
import * as types from '../constants/ActionTypes';

export function addTodo(text) {
  return { type: types.ADD_TODO, text };
}
import React, { PropTypes, Component } from 'react';
import classnames from 'classnames';
import { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } from '../constants/TodoFilters';
import 'http://material.js'
…
export default Footer;
export const ADD_TODO = 'ADD_TODO';
export const DELETE_TODO = 'DELETE_TODO';
export const EDIT_TODO = 'EDIT_TODO';




					

Destruct

						
const { activeCount } = this.props;
…
const { filter: selectedFilter, onShow } = this.props;
const [, filters] = output.props.children;
…
const [,, clear] = output.props.children;
var { op: a, lhs: { op: b }, rhs: c } = getASTNode();
Détails



					

Les strings interpolation et multi lignes

						
const person = { first: 'Thomas', last: 'Anderson', age: 25, nickname: 'Neo' };

// Interpolation de JS quelconque
console.log(`${person.first} aka ${person.nickname}`)
// => 'Thomas aka Neo'

// Multi-ligne !
const markup = `
  • ${person.first} ${person.last}, age ${person.age}
  • `;

    Valeur par défaut dans une fonction

    						
    function add(source, numToAdd = 1){
    	return source + numToAdd;
    }
    						
    
    
    					

    Les objets

    						
    function editTodo(id, text) {
      return { type: types.EDIT_TODO, id, text };
      //On créé un objet avec pour clef le nom de la variable.
    }
    const FILTERS = {
      [maVar1]: 'All',
      [maVar2]: 'Active',
      [maVar3]: 'Completed'
    };
    						
    
    
    					

    ES6 / 2015

    Surtout du sucre syntaxique dans son utilisation

    LODASH / UNDERSCORE

    on ne réinvente pas la roue

    Les trucs utiles

    • Est ce que ma variable est un nombre / function / tableau lodash/lang
    • Filtrer, trier, reduce.... lodash/{collection/object/array}
    • Plein de fonctions prêtes à l'emploi lodash/function

    MOMENT

    on ne réinvente pas la roue

    Les trucs utiles

    • formatter des dates moment().format('L')
    • Manipuler des dates moment(maDate).add('days', 1)
    • Timezone...
    Si tu manipules des dates tu utilises momentjs

    Exercices