Pages

this et map and Co

Perte du this

var Sum10 = {
  prop: 10,
  addPropTo: function(array) {
    return array.map(function(elt) {
      return this.prop + elt;
    });
  }
};
           
console.log(Sum10.addPropTo([5,10,15]));
Le code ne marche pas !

Plus surprenant est d'exécuter le code suivant

window.prop = 10;




Don't worry : il y a perte du this. Problème classique que l'appel à la fonction bind peut résoudre facilement.

var Sum10 = {
  prop: 10,
  addPropTo: function(array) {
    return array.map(function(elt) {
      return this.prop + elt;
    }.bind(this));
  }
};
                     

console.log(Sum10.addPropTo([5,10,15]));

et puisque ce problème est classique en JS. Il suffit d'appeler les fonctions de type map avec un argument supplémentaire : this.

var Sum10 = {
  prop: 10,
  addPropTo: function(array) {
    return array.map(function(elt) {
      return this.prop + elt;
    },this);
  }
};
                     


console.log(Sum10.addPropTo([6,11,15]));



En action


objet littéral : get et set

Pour des objets littéraux les fonctions get et set permettent d'accéder à des propriétés.

var objLitteral = {
  elements: ["P1", "P2", "P3"],
  get nb() {
    return this.elements.nb || this.elements.length;
  },
  set nb(value) {
    this.elements.nb = value;
  }
};

console.log(objLitteral.nb);
objLitteral.nb = 10;
console.log(objLitteral.nb);

map, reduce : en action

Compléter le code suivant pour obtenir un alignement des colonnes.


JS Bin on jsbin.com

Recherche d'un IDE

http://www.slant.co/topics/1686/~javascript-ides
http://www.sitepoint.com/top-javascript-frameworks-libraries-tools-use/?utm_source=javascriptweekly&utm_medium=email

TP : tableau


Récupérer les données


JS Bin on jsbin.com

Calcul des moyennes d'age par sex

exemple pour sex=M
  • nb =21
  • age = [73, 34, 90, 72, 47, 91, 28, 40, 67, 66, 63, 45, 68, 75, 71, 45, 54, 92, 41, 60, 73]
  • moyenne = 62

Affichage

Name sex born died age ecart/moyenne
dupont znz 1832 1905 73 11
dupond f 1876 1905 29 -26
dupont ana 1850 0 0 0

Des fonctions de haut vol : pourquoi faire ?




var min = ancestry[0];
for (var i = 1; i < ancestry.length; i++) {
  var cur = ancestry[i];
  if (cur.born < min.born)
    min = cur;
}
console.log(min);

Fonctions de filtre

function filter(array, test) {
  var passed = [];
  for (var i = 0; i < array.length; i++) {
    if (test(array[i]))
      passed.push(array[i]);
  }
  return passed;
}


code


AJAX + fx on jsbin.com

Nous pourrions ne pas utiliser tab.foreach, mais écrire nous même une fonction équivalente.

function map(array, transform) {
  var mapped = [];
  for (var i = 0; i < array.length; i++)
    mapped.push(transform(array[i]));
  return mapped;
}

voici une bibliothèque qui implémente map
https://lodash.com/docs#map


code 



: https://github.com/lodash/lodash/blob/3.10.1/lodash.src.js#L6864
JS Bin on jsbin.com

Closure : one more


me = {
   name : "denis",
   age : 50
}

you = {
   name : "anonymous",
   age : 45
}


function greaterThan(n) {
  return function(m) { return m > n; };
}
var greaterThanMe = greaterThan(me.age);
console.log(greaterThanMe(you.age));



fonction en argument


function unless(test, then) {
  if (!test) then();

}

function repeat(times, fn) {
  for (let i = 0; i < times; i++) fn(i);
}

repeat(100, function(n) {
  unless(n % 2, function() {
    console.log(` ${n} est un nombre pair ! `);
  });
});

For each


Nous pouvons écrire notre proche forEach

Code


function forEach(array, action) {
  for (var i = 0; i < array.length; i++)
    action(array[i]);
}

JS Bin on jsbin.com

Code d'une bibliothèque


lodash

Ecriture de boucle : sur des objets

Object.defineProperty(Object.prototype, "hiddensweetHome",
                      {enumerable: false, value: "lune"});

Enumerable on jsbin.com

Ecriture de boucle

Il est recommandé d'écrire les boucles sous cette forme :

for (var name in map) {
  if (map.hasOwnProperty(name)) {
    // ... this is an own property
  }
}

get et set : En Action

Exemple d'utilisation : 

Object.defineProperty(this, 'val', {

    get: function() {
      return val;
    },
    set: function(value) {
      val = value;
      archive.push({ value: val });
    }
  });

JS Bin on jsbin.com

Différence entre fonction expression et fonction déclaration !

Voici deux déclarations de fonction.
  1. Fonction déclaration : message
  2. Fonction expression : mes

console.log(typeof message);// function
console.log(typeof mes); // undefined

// 1
function message(){
  console.log("hi");
}

// 2
var mes = function (){
  console.log("hi");
};

Nous allons étudier les différences de ces deux déclarations.

Portée

La fonction message est disponible dès que le programme est chargé, alors que la fonction mes ne sera disponible qu'après son affectation. 

Le test typeof des types en début de programme le démontre.

Autrement dit en début de programme, vous pouvez utiliser message mais pas mes


Syntaxe

Une autre différence est la présence du point virgule lors de la définition de mes ; il s'agit en effet d'une affectation, il faut donc mettre un point virgule.


Argument

Un des intérêt d'utiliser l'affectation est que la fonction peut être passée en argument d'une autre fonction.

wellcome( mes ); // affiche "hi"

function wellcome ( textFx ){
  textFx ();
}


intérêt

Un autre intérêt est le dynamisme de l'affectation. 

En voici un exemple :

let mes,
    AM = true;

if ( AM ) {
    mes = function (){
      console.log("good morning");
    };
}

else { //pm
   mes = function (){
      console.log("good afternoon");
   };
}

wellcome ( mes );
function wellcome ( text ){
  text ();

}



JS Bin on jsbin.com

Babel

react

JS Bin on jsbin.com

test : portée

Imaginer les affichages :

var a = 1;  

function outer() { 
  var b = 2;   
  console.log(" test 1 ", a, b);    
  function inner() {       
    a = 3;  
    b = 4;   
    c = 5;  
  }         
  inner();    
  console.log(" test 2 ", a, b, c);  
}
outer();                  
console.log(" test 3 ",a, b);


JS Bin on jsbin.com

Hoisting : le saviez vous ?

hoisting = La remontée de variables

Les déclarations de variables sont traitées avant que n'importe quel autre code soit exécuté. Les affectations sont réalisées après.

var x;
x = 2;

est équivalent à

x = 2;
var x;

Comprenons les exemples suivants.

La variable a n'est pas déclarée = error



La variable b est déclarée et affectée



La variable b est déclarée (hoisting) mais pas affectée. il n'y a donc pas de message d'erreur.


Notez l'équivalence des deux fonctions suivantes :

function testHoisting (cond){

  if (cond) {
    console.log(b);
  }
  else {
    var b = 2;

  }  
}

function testHoisting (cond){
  var b;
  if (cond) {
    console.log(b); // ici b est déclaré, pas affecté.
  }
  else {
    b = 2;
  }  
}



Introduction de let : retour vers la notion de portée de bloc.

function testHoisting (cond){
   
  if (cond) {
    console.log(b);
  }
  else {
    let b = 2;
    console.log(b);
  }    
}

🪳testHoisting(true);
🆒testHoisting(false);

myClassList : Ecriture d'une API

Nous allons écrire une classe JS simulant classList.

JavaScript
ptr = object.classList



C'est un vrai exercice de programmation.

Nous allons juste développer la méthode add et donnerons le reste du code à étudier.

Nous savons récupérer les classes associées à un nœud du DOM.
HTML<element class="p" ... >
JavaScript
Notons que le type retourné est "string"

typeof(object.className)


Mise en place de la fonction add

Pour manipuler (ajouter, supprimer) un string, il est fréquent de transformer le string en tableau pour utiliser les méthodes efficaces sur les tableaux.

  1. transforme string en tableau
  2. ajoute la class au tableau
  3. transforme tableau en string

Voici les fonctions de base

var t = document.getElementById("f");
var classTab,
    classString;
console.log(" type of = " , typeof(t.className));

// string->tab
classTab = t.className.split(" ");
console.log(classTab);

//tab->string
classString = classTab.join(" ");
console.log(classString);


JS Bin on jsbin.com

Nous pouvons tester la présence d'une classe avant ajout

JS Bin on jsbin.com

Amélioration du code

Cela marche, parfait, mais c'est maintenant que la partie intéressante commence !
  1. Créer des fonctions pour réutiliser notre code de base
  2. Introduire des éléments de test
Création de fonctions de base

    var classesSeparator = " ";

    function splitClasses(classesString) {
        return classesString.split(classesSeparator);
    }

    function joinClasses(classes) {
        return classes.join(classesSeparator);
    }

    function indexInClasses(className, classes) {
        if (!className || !classes || !classes.length)
            return false;
        return classes.indexOf(className);
    }
JS Bin on jsbin.com

Amélioration du code

Il est foncdemental d'imaginer que ce code va rentrer en conflit avec une autre bibliothèque JS. Imaginer que notre fonction add soit utilisée ainsi add(1,2) ?

Les améliorations consistent


  1. Création d'un espace de noms
  2. Mise en évidence des fonctions internes
  3. Retour d'un objet pointant sur les fonctions

Création d'un espace de nom

Classes = (function () {
    //code
})();

Mise en évidence des fonctions et variables internes

Nous marquons ces items avec un _

exemple :
 var _classesSeparator = " ";


Retour de l'objet

La partie la plus subtile.

Classes = (function () {
...
    return {
        "add": add
    };
})();

Le  principe reste simple. 

Une fonction anonyme s'auto exécute et renvoie un objet que l'on affecte à Classes.
Classes est un objet qui a un méthode add qui pointe en réalité sur la méthode add que nous avons définie et qui fait appelle à des fonctions internes qui ne sont plus atteignable : TOP !


JS Bin on jsbin.com

Pour finir, on peut evisager d'écrire l'ensemble des fonctions utiles.
  JS Bin on jsbin.com

:nth-child


:nth-child ( pos )
Le style est appliqué à toutes les balises enfants en position pos.

pos = ( 2 )

<body> // second enfant de HTML
<div>
  <h2>1 enfant</h2> 
  <p>2 enfant</p> // second enfant de div
  <h2>3 enfant</h2>
  <h2>4 enfant</h2>
</div>
</body>


p:nth-child ( pos )
Le style est appliqué à toutes balises p enfants en position pos.

pos = ( 2 )

<div>
  <h2>1 enfant</h2> 
  <p>2 enfant</p> // second enfant de div
  <h2>3 enfant</h2>
  <h2>4 enfant</h2>
</div>
</body>


JS Bin on jsbin.com

les sélecteurs : revision

les arguments de querySelectorAll

JS Bin on jsbin.com

Closure

Une des caractéristiques de la  "closure" est que le context n'est jamais inclue dans la closure !

 id = "val";

$( '*' ).each( function () {
     console.log( id );
}

 this .id = "val";
$( '*' ).each( function () {
     console.log(  this .id );
}

 this  est en réalité lié à chaque élément de la boucle. LA correction sera de créer une copie qui est inclue dans la closure.

this.id = "val";
var outer = this;
$( '*' ).each( function () {
     console.log( outer.id );

Paramêtres des fonctions


Un argument passé par valeur est copié, et c'est la copie qui est manipulée par la fonction. Ainsi toute modification de cette copie n'entraîne donc aucun changement dans la donnée d'origine. Ce qui n'est pas le cas pour un passage par référence.

Comment sont passés les arguments en JS ?

 les variables ou objets sont toujours passés par valeur


Mais la valeur dépend du type de l'argument. 

Pour les types natifs (number, string ....), c'est par valeur et pour les tableaux et autres objets se sera la valeur de la référence.

Passage valeur

let age = 50;

function changeAgeNatif(age) { 
  age++; 
}
function changeAgeObject(person) {
 person.age++;  


changeAgeNatif(age);
console.log(age);


Passage référence

function Person(age) {
    this.age = age;
}

let denis = new Person(50);

function changeAgeObject(person) {
  person.age++; 
}

changeAgeObject(denis);
console.log(denis.age);


fichier⏩


  JS Bin on jsbin.com

Des fonctions peuvent être passées en arguments


exemple :

var values = [0,3,2,1];
console.log(values);

values.sort(function(value1,value2){ return value2 - value1; });

console.log(values);

fichier2⟴


Cas des fonctions en argument : perte du contexte.

function Person(age) {
    this.age = age;
}

Person.prototype.changeAgeObject = function(){
     this.age++;
};

let denis = new Person(50);


denis.changeAgeObject();
console.log(denis.age);

// passage fonction en argument et perte du this

function test(methode) {
    methode();
}

test(denis.changeAgeObject);
console.log(denis.age);



En Javascript, quand on passe une méthode en argument, on perd le contexte. 



Voici la correction, il faut explicitement lier la fonction au contexte !


test(denis.changeAgeObject.bind(denis));
console.log(denis.age);


 

On s'accroche voici le code de bind :

Function.prototype.bind = function(obj) { 
  var method = this;
   temp = function() { 
    return method.apply(obj, arguments); 
   }; 
  
  return temp; 
};

Comprendre ce code est l'objectif de J. rezig dans son livre Javascript Ninja

Paramètres sur les events


John Resig

 This tutorial contains code and discussion from the upcoming book Secrets of the JavaScript Ninja by John Resig.

Evenements sur des inputs : have fun

Entrez successivement quatre entrées différentes !





Cette exemple montre que nous pouvons développer nos propres événements sur les inputs.

Amusons nous en créant des massages délirant lorsque l'utilisateur ne tape pas le même adresse !

var messages=["Verifiez  vos adresses. Merci.",
                         " Soyez attentif, merci",
                         "Vous commencez à me fatiguer",
                         "Bravo, j'abandonne !"]

1) recupération de l'input

var mailC = document.getElementById('mailC');


2) mise en place de l'écouteur

mailC.addEventListener('change', checkPasswordValidity, false);


3) définition du gestionnaire

var checkPasswordValidity = function() {
        if (mail.value != mailC.value) {
           
           compteur=++compteur%4;
           mailC.setCustomValidity(messages[compteur]);
          
        } else {
            mailC.setCustomValidity('');
        }        
    };
 
 
 

    

les patterns utiles : verification d'un mot de passe.


Vérification par les patterns
    Le pattern .*[\d] vérifie les motifs
  • 2ab : (* = 0 ; d = 2)
  • a2b : (* = 1)
  • ab2 : (* = 2)
  • abc : (erreur)
    Le pattern .*[a-z] vérifie les motifs
  • 2ab : (* = 1)
  • a2b : (* = 0)
  • ab2 : (* = 0)
  • 222 : (erreur)
Nous pourrons écrire pattern="^(?=.*[a-z])(?=.*[\d]).*$" pour vérifier les conditions au moins un caractère et un nombre.

^                   # Start of string
(?=                 # groupe
 .*[a-z]            #  au - 1 lettre qq part
)                   #  
(?=                 # groupe
  .*[\d])           #  au - un chiffre qq part
.*                  # 
$                   # End of string
    Autres pattern
  • (?=.{8,}) : au - 8 Entrées
  • (?=.*[A-Z]) : au - 1 Majuscule
  • (?=.*[\W]) : au - 1 Caractère Spécial/li>
Découvrez une liste de patterns : https://developers.google.com/web/fundamentals/input/form/provide-real-time-validation