Subtilités et curiosités notables en Javascript

Le contenu de cet article ne se veut pas du tout élitiste, mais soyons clair, il ne s’adresse qu’a une infime minorité d’individus côtoyant le développement régulièrement. Les différentes curiosités qui y sont jugées amusantes, ne sont pas de bonnes grosses blagues, mais plutôt comme la mise en avant de comportements inattendus ou imprévisibles par rapport à d’autre langages, ou tout simplement illogiques.

les Variables et pointers

Les nombres

Javascript gère les nombres de manière particulière, et nous retourne parfois des résultats très différents de ce que l’on a appris en cours de Mathématiques. Ces incohérences arrivent surtout lorsque l’on manipule des nombres décimaux :
console.log(2.3 + 2.4); // Display : 4.699999999999999
La raison de ce résultat est une histoire de byte, et ne m’étendrai pas plus sur le sujet. Cependant cette curiosité n’est pas une fatalité car Javascript nous propose une petite fonction bien pratique à savoir .toFixed(a) (ou a correspond au nombre de chiffre après la virgule que nous souhaitons). En pratique ça donne quelque chose comme ça :
var myNumber = (2.3 + 2.4).toFixed(1);
console.log(myNumber); // Display : 4.7

Les chaînes de caractères

var a = "coucou";
var b = a;
a = "hi";

console.log(a,b) // display : "hi" , "coucou"

Dans les l’exemples ci-dessus (qui est aussi valable avec des nombres), javascript ne crée pas de lien entre a et b. Les deux restent parfaitement indépendants. Mais ce n’est pas le cas avec tous les types de données. En fait, c’est même valable uniquement avec les données de type Number ou String

Les objets

var a = {data:'coucou'};
var b = a;
a = {data:'hi'};

console.log(a,b) // display :  Object {data: "hi"} , Object {data: "coucou"}

Pour l’instant ça se comporte comme précédemment, changeons un peu la donne :

var a = {data:'coucou'};
var b = a;
a.data = 'hi';

console.log(a,b) // display : Object {data: "hi"} Object {data: "hi"}

Vous avez noté ? les variables a et b son liées lorsque l’on modifie leur contenu, et non pas directement leur valeur immédiate. Il en est de même pour les Array.

Les tableaux

var a = ["coucou"];
var b = a;
a[0] = 'hi';

console.log(a,b) // display : ["hi"] ["hi"]

Les fonctions d’un objet

Les objets peuvent être constitués de données mais aussi de fonctions. Et s’il est courant et évident d’accéder à une donnée de la manière suivante : myObject[« myKey »] , sachez que la même syntaxe peut être utilisée pour appeler une fonction :

var myObject = {
    myFunction:function(){ }
);

est accessible depuis :

myObject.myFunction();

ou :

myObject["myFunction"]();

ou encore :

var methodToExecute = "myFunction";
myObject[methodToExecute]();

récursion possible

Les comportements ci-dessus peuvent aboutir à des situations amusantes :

var a = {data:"coucou"};
var b = a;
a.data = b;

console.log(a) // display :  {data:{data:{data:{data:{data:{data:{data:{data:{data:{data:{data: ... }}}}}}}}}}}

Vous venez de créer un objet qui fait référence à lui-même. Donc faites attention.

Les opérateurs

Le signe ! inverse la valeur booléenne

Le signe ! permet d’inverser la valeur booléenne de la donnée qui suit. Cela permet d’obtenir les résultats suivants :

console.log( true ) // true
console.log( !true ) // false
console.log( !!true ) // true

Ca marche aussi avec des valeurs non booléennes :

console.log( 0 ) // 0
console.log( !0 ) // true
console.log( !!0 ) // false

bien que console.log("0" == 0) retourne true :

console.log("0" == 0) // true

Du fait que l’on ait une chaîne en entrée, Javascript est capable de l’interpréter en Number (on parle de transtypage), mais si on la manipule, les comportements deviennent différents

console.log( "0" ) // 0
console.log( !"0" ) // false
console.log( !!"0" ) // true

Ces résultats ne sont pas systématiques pour toutes les chaînes :

console.log("0" == 0) // true
console.log("" == 0) // true
console.log("0" == "") // false

console.log( "" ) // ""
console.log( !"" ) // true
console.log( !!"" ) // false

Etonnant non ? On va faire une pause avec !. Une autre chose que l’on peut faire en Javascript que d’autres langages ne nous permettent pas forcément.

La condition ternaire

La condition ternaire est très connue, et c’est toujours assez classe d’en glisser une ou deux dans son code :

var a = (Math.random() > .5 ) ? 'yes' : 'no';
console.log(a) // display 'yes' or 'no'

La valeur a prendra la valeur yes ou no en fonction de la condition préalable. Mais bien que pratique, rien de nouveau. Voici quelque-chose de plus singulier :

console.log( true || true || "coucou") // display true
console.log( false || false || "coucou" ) // display "coucou"

console.log( false && false && "coucou" ) // display false
console.log( true && true && "coucou") // display "coucou"

Supposons le code suivant, où a possède la valeur False si test(1) et test(2) échouent, et si les deux tests réussissent, alors a prend la valeur coucou :

var a = false;
if( test1() ){
    if( test2() ){
        a = "coucou";
    }   
}

La syntaxe ci-dessus est clairement contestable, mais le but ici est de montrer les syntaxes alternatives que propose Javascript :

var a = false;
if( test1() && test2() ){
    a = "coucou";   
}

ou encore :

var a = ( test1() && test2() ) ?  "coucou" : false;

ou finalement :

var a = test1() && test2() && "coucou";

<< et >>

Voici deux opérateurs répandus dans les différents langages de programmation, mais pas souvent utilisés. << et >> ont chacun leur propre définition barbare :

<< : Rotation à gauche Décale les bits vers la droite (divise par 2 à chaque décalage). Les zéros qui sortent à droite sont perdus, tandis que le bit non-nul de poids plus fort est recopié à gauche
>> : Rotation à droite avec conservation du signe Décale les bits vers la droite (divise par 2 à chaque décalage). Les zéros qui sortent à droite sont perdus, tandis que le bit non-nul de poids plus fort est recopié à gauche

Pour faire simple, a << b correspond à : Math.floor(a * Math.pow(2,b)), c’est à dire « a fois 2 puissance b, arrondi à l’entier inférieur. Exemples :

 1 << 1 // 1*2 soit 2
1 << 2 // 1*2*2 soit 4
.5 << 2 // 1*2*2 soit 2
...
3 << 10 // 3072

Dans ce cas, a >> b correspond à : Math.floor(a / Math.pow(2,b)), c’est à dire « a divisé par 2 puissance b, arrondi à l’entier inférieur. Exemples

 1 >> 1 // 1/2 = 0.5 arrondi à l'inférieur, soit 0
256 >> 2 // 1/(2*2) soit 16
1024 >> 8 // 1(2*2*2*2*2*2*2*2) soit 4

null n’est pas nul (mais il reste pénible)!

la valeur null est utilisée pour spécifier qu’un contenu est défini, mais vide. Il est bon de préciser que null n’a rien à voir avec nul qui veut dire 0 en mathématiques. Donc pour reprendre :

null != undefined // false
null !== undefined // true
null != 0 // true

Mais là où ça devient amusant, c’est que null peut être comparé à des nombres, et là, surprise :

null >= 0 // true
null <= 0 // true
null < 1 // true
null > -1 // true

Pas besoin d’avoir fait de longues études pour déduire de ces quatre dernières lignes que null devrait être égal à 0. Mais là, Javascript nous réserve une surprise :

null == 0 // false

Ce qu’il faut en conclure, c’est que dès que vous comparez deux données, il est très important de privilégier un opérateur d’identité qui permet de comparer deux valeurs ( === ou !== ), mais aussi leur type !

Un dernier pour la route

Javascript is werd

J’ai Trouvé cet article sur Reddit, je vous laisse le plaisir de consulter le fil de discussion que génère cette capture : Please don’t hate me javascript devs

Pour aller un peu plus loin

Voici des matrices de comparaison des différents types de données que l’on peut retrouver en javascript. L’utilité des ces tableaux est toute relative, mais les motifs générés sont amusants.

td, th{ font-size: 12px; font-weight: normal; } td{ padding:0; overflow: hidden; } th{ text-align: right; } th.operator{ vertical-align: bottom; } th.rotate { height: 47px; white-space: nowrap; } th.rotate > div { transform: translate(9px, 10px) rotate(-90deg); width:10px; } th.rotate > div > span { border-bottom: 1px solid #ccc; padding: 5px 10px; } table{ margin: 50px auto ; border-spacing:0; width:100%; } td.boolean div{ display:block; border-radius: 4px; padding-bottom: 80%; } td.true div{ margin: 4% auto 0; text-align: center; width:80%; background-color:green; border:1px solid green; } td.false div{ margin: 4% auto 0; text-align: center; width:80%; border:1px solid #CCC; } // <!– // clés à comparer : var keysToCompare = { "true": true, "1": 1, "!0": !0, "\"1\"": "1", "!\"\"": !"", "false": false, "0": 0, "!1": !1, "\"\"": "", "\"0\"": "0", "!\"1\"": "!1", "!\"0\"": "!0", "[]": [], "Infinity": Infinity, "undefined": undefined, "function(){}":function(){}, "null": null, "NaN": NaN, "{}": {}, }; function buildMatrix(keys , operator){ var html = "

L’opérateur « +operator+ »

« ; html += « « ; var f = 15; // header html += « « ; html += ‘‘; var u = 0 for (i in keys) { u++ html += ‘‘; } // content html += « « ; for (i in keys) { l = 100; html += « « ; l-=f; html += ‘« ; console.log(l+ » / « +u) var w = l / u; for (y in keys) { var r = eval(« var a= »+i+ »;var b = « +y+ »; (a « + operator + »b); » ) ; var style = r ? ‘true’ : « false » html += ‘« ; } html += « « ; } html += « 
‘ + i + ‘
‘ + i + « ‘ + ‘
‘ + « 
« ; return html } // –> document.write(buildMatrix(keysToCompare , « == »)) document.write(buildMatrix(keysToCompare , « != »)); document.write(buildMatrix(keysToCompare , « === »)); document.write(buildMatrix(keysToCompare , « !== »)); document.write(buildMatrix(keysToCompare , ‘\&=’.replace(/#038;/g, »))); document.write(buildMatrix(keysToCompare , ‘\&\&’.replace(/#038;/g, »))); document.write(buildMatrix(keysToCompare , « || »)); document.write(buildMatrix(keysToCompare , « > »)); document.write(buildMatrix(keysToCompare , « = »)); document.write(buildMatrix(keysToCompare , « <="));

Je ne suis pas persuadé que cet article va vous permettre de briller en société, mais j’espère qu’il pourra vous servir pour vos développements futurs.

Erwan SABOURIN