Javascript – Les bases – Les outils



Javascript – Les bases – Les outils

4 0


formation-js

Support for the training of best practices on technology : js, es6, React, ...

On Github get-focus / formation-js

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

					
<menu id="file" value="File">
  <popup>
    <menuitem value="New" onclick="CreateNewDoc()"></menuitem>
    <menuitem value="Open" onclick="OpenDoc()"></menuitem>
    <menuitem value="Close" onclick="CloseDoc()"></menuitem>
  </popup>
</menu>
					
				

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) On part de l’objet indexé (obj) Si on trouve prop dans ses own properties, on s’arrête là Sinon, on passe sur le prototype du niveau supérieur : celui du constructeur de l’objet en cours* 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 = `<li>
${person.first} ${person.last}, age ${person.age}
</li>`;

					


					

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

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