PROJET AUTOBLOG


Sam et Max

source: Sam et Max

⇐ retour index

Mise à jour

Mise à jour de la base de données, veuillez patienter...

La fonction anonyme appelée immédiatement en Javascript: (function())()

mercredi 2 janvier 2013 à 18:09

Javascript est un langage qui a plein d’idiomes bien à lui. En effet, c’est un langage très puissant, et ausi plein de couilles velues planquées ici et là. Les ninjas JS ont donc créée des astuces pour pallier à ces problèmes, en utilisant la force de leur outil.

Un des gros soucis en JS, c’est qu’il dépend beaucoup des variables globales, qui sont une grosse source d’ennuis en tout genre. Et il est très facile de déclarer ou d’utiliser une variable globale par erreur.

Pour limiter ce problème, on utilise la technique de la fonction anonyme immédiatement appelée. Cela fonctionne comme ceci, au lieu de faire:
pre lang=”javascript”>
alert(‘rouge’);

On fait:

(function(){
    alert('rouge');
})()

Explication pas à pas

En effet, en Javascript on peut créer une fonction sans lui donner de nom. C'est ce qu'on appelle une fonction anonyme:

function(){
    alert('rouge');
}

On peut attribuer cette fonction à une variable.

var une_variable_qui_passait_par_la = function(){ alert('rouge')};

Et derrière on peut appeler la fonction ainsi:

une_variable_qui_passait_par_la();

Mais dans notre cas, on ne met pas la fonction dans une variable, on l'enferme dans des parenthèses, ce qui garde la référence le temps de l'appeler, et on l'appelle. On fait la même chose que précédement en fait, on saute juste une étape :

(function(){ alert('rouge');})()

La référence à la fonction est perdue, on ne pourra pas l'appeler de nouveau. Mais on s'en branle fortement, on voulait juste l'appeler une fois de toute façon.

Pour rendre le code plus lisible, on le met sur plusieurs lignes:

(function(){
    alert('rouge');
})() /* on éxécute la fonction cash pistache */

Maintenant pour comprendre ce que ça fait, il faut juste réaliser que le bout de code ci-dessous fait EXACTEMENT la même chose que le bout de code juste au dessus :

alert('route');

Car on déclare la fonction, mais on l'éxécute tout de suite. Donc dans les deux cas, au chargement du script, un aura une alerte immédiatement.

Mais putain si ça fait la même chose, pourquoi tu nous gonfles avec ça ?

Parceque dans le cas du dessus ça fait la même chose, mais ça a des propriétés en plus. Vous vous doutez bien qu'on tape pas 4 lignes en trop pour le plaisir (à part en Java).

En effet, en créant une fonction, on crée un scope. Ainsi, tout ce qui est déclaré avec var dans la fonction sera garanti de ne pas être une variable globale. Pas d'effet de bord sur le monde extérieur. Et le monde extérieur n'a PAS accès aux variables déclarées dans la fonction. Par contre la fonction a accès aux variables globales !

Par exemple, dans un navigateur, vous avez toujours accès à l'objet window et l'objet Date partout dans le code.

/*
    Ici vous avez accès à window et Date
*/
 
(function(){
 
    /* Ceci ne va pas changer l'objet window en dehors de la fonction
        car on redéclare la variable, à l'intérieur du scope de la fonction.
     */
    var window = '3.1';
 
    var rideau = 'vista';
 
    /* Ici vous avez accès à Date */
 
    alert(new Date())
 
 
})()
 
/*
    Ici vous n'avez PAS accès à "rideau"
*/

Il est donc une bonne pratique de mettre TOUT son script dans une fonction qui s'auto-appelle, pour l'isoler du monde exterieur et ne pas pourrir le namespace global. Si on avait pas fait ça, et qu'un autre script définissait aussi rideau, l'un des deux aurait écrasé l'autre. Et dans tous les cas on aurait écrasé window.

Encore mieux

Comme on crée un conteneur isolé, on peut faire ce que l'on veut dedans. Et on a deux moyens de communique avec ce conteneur. D'abord, les variables globales. Ensuite, les paramètres de la fonction auto-appelée.

On peut exposer le contenu de la fonction à travers les variables globales. Par exemple, vous faites un générateur de 'pouet', chose importante s'il en est :

(function(){
 
    /* ces variables ne sont pas accessibles depuis l'extérieur */
    var pouetGenerator = {};
    var variablePrivee = true;
 
    /* ces fonctions sont accessibles seulement depuis pouetGenerator */
    pouetGenerator.fairePouet = function(){
        alert('pouet')
    }
 
    pouetGenerator.nePasFairePouet = function(){
        if (variablePrivee) {
            alert('pas pouet');
        }
    }
 
    /* On rend notre pouetGenerator accessible au reste du monde */
    window.pouetGenerator = pouetGenerator;
 
})()
 
/* On peut utiliser notre générateur de pouet partout ailleurs */
pouetGenerator.nePasFairePouet()

En effet, tout ce qui est attaché à window devient une variable globale dans un navigateur. C'est une caractéristique de Javascript dans un navigateur Web: window est un objet accessible partout, et on a accès partout à ses attributs. On a donc notre code parfaitement isolé (la seule chose exposée c'est notre point d'entrée : pouetGenerator). On ne pollue pas le namespace. En prime personne n'a accès aux variables privées comme variablePrivee;

On peut aussi utiliser les paramètres et les valeurs de retour pour cela :

/* pouetGenerator, déclaré sans "var", est une variable globale */
pouetGenerator = (function(chaine_du_pouet){
 
    /* ces variables ne sont pas accessibles depuis l'extérieur */
    var pouetGenerator = {};
    var variablePrivee = true;
 
    /* ces fonctions sont accessibles seulement depuis pouetGenerator */
    pouetGenerator.fairePouet = function(){
        alert(chaine_du_pouet)
    }
 
    pouetGenerator.nePasFairePouet = function(){
        if (variablePrivee) {
            alert('pas pouet');
        }
    }
 
    /* On rend notre pouetGenerator accessible au reste du monde
        en la retournant, ce qui va mettre la référence dans le pouetGenerator
        qui est la variable globale.
    */
    return pouetGenerator;
 
/* On passe 'pouet' en paramètre, il va se retrouver dans chaine_du_pouet */
})('pouet')
 
/* On peut utiliser notre générateur de pouet au même niveau */
pouetGenerator.nePasFairePouet()

Il y a deux différences dans ce code. La première, c'est qu'on met la variable à disposition en la retournant plutôt qu'en l'attachant à document. La seconde, c'est qu'on passe 'pouet'.

On pourrait se dire que l'interêt est limité, mais considérez l'exempe suivant:

(function($){
    $('p').css('color', 'rouge-fluo')
})(jQuery.noConflic())

jQuery.noConflic() restaure $ à sa valeur précédente, permettant à d'autres libs d'utiliser $ (comme la lib prototype.js). Mais jQuery.noConflic() retourne aussi l'objet jQuery, que l'on passe en paramètre. Ce paramètre est nommé $ dans la signature de la fonction. Dans notre fonction, on peut donc QUAND MÊME utiliser $ pour faire référence à jQuery car tout ce qui est dans la fonction est isolée du reste du monde. Et le reste du monde peut utiliser $ pour autre chose que jQuery.

Best practice

Généralement un code de production pour un script complet ressemble donc à ça:

;(function(alias){
"use strict";
 
/* tout le code du script va ici */
 
var une_variable_privee;
 
window.une_variable_exposee = 'foo';
 
})(truc_a_aliaser)

Notez le ; au début qui permet de rajouter votre script à la suite d'un autre script même si celui-ci a oublié de mettre un ; sur sa dernière ligne (utile pour les minifieurs).

Notez aussi le "use strict"; qui dit au navigateur que tout ce qui se trouve dans cette fonction devra être traité par un parseur strict qui vous signalera plus d'erreurs de code. Cela ne s'applique qu'à la fonction, laissant le reste du monde le droit d'utiliser du code moins strict.

Sortir de plusieurs boucles for imbriquées en Python

mardi 1 janvier 2013 à 20:27

Le mot clé break permet de sortir d’une boucle for abruptement. Mais une seule. Parfois on a 3, 4 boucles imbriquées, et on aimerait tellement sortir de toutes d’un coup.

Ce que je vais vous montrer est mal. Mais c’est tellement bon.

# on fait une exception qui hérite de StopIteration car c'est ce qui est utilisé
# de toute façon pour arrêter une boucle
class MultiStopIteration(StopIteration):
    # la classe est capable de se lever elle même comme exception
    def throw(self):
        raise self
 
 
@contextmanager
def multibreak():
    # le seul boulot de notre context manager c'est de donne le moyen de lever
    # l'exception tout en l'attrapant
    try:
        yield MultiStopIteration().throw
    except MultiStopIteration:
        pass

En gros on se créé un petit context manager, dont le seul but est de créer une exception qui va remonter en pêtant toutes les boucles. Je vous avais dit que c’était mal

Ca s’utilise comme ça:

>>> with multibreak() as stop:
...     for x in range(1, 4):
...         for z in range(1, 4):
...             for w in range(1, 4):
...                 print w
...                 if x * z * w == 2 * 2 * 2:
...                     print 'stop'
...                     stop() # appel MultiStopIteration().throw()
...
1
2
3
1
2
3
1
2
3
1
2
3
1
2
stop

Je vous avais dit que ça serait bon.

A part le fait que ce n’est pas très rapide au moment du bubbling de l’exception sur 3 blocks, il n’y a aucun danger ou side-effect. On triche en fait à peine, car le mécanisme interne des boucles en Python utilise de toute façon déjà une exception (StopIteration) pour dire à une boucle quand s’arrêter.

Bref, encore une victoire de connard.

Dis papa, dis papa, dis-moi, dis-moi. Comment c’est fait dans une boucle for ?

lundi 31 décembre 2012 à 17:10

Dis papa, dis papa, dis-moi, dis-moi. Comment c’est fait dans une boucle for ?

C’est pas compliquéééééééééééé, j’vais tout t’expliquuuuuerrrrrrrrrr.

C´est le p´tit zinzin qui passe par ici:

>>> class MonIterable(object): # faisons notre propre itérable
...
...     def __init__(self):
...         self.values = [1, 2]
...
...     def __iter__(self): # ('for' appelle __iter__ automatiquement)
...         return self # __iter__ doit renvoyer un iterateur, ici nous-même
...
...     def next(self): # chaque tour de boucle, for appelle next()
...         if self.values: # qui retourne une des valeus de self.values
...             return self.values.pop() # en l'enlevant de la liste initiale
...         raise StopIteration() # si il y en a plus, il dit stop !
...

Et qui va toucher le p´tit machinnnnnnnnnnnnnnnnnnnnnnnnn !

>>> for x in MonIterable(): # ceci appelle next() jusqu'à StopIteration
...    print x
2
1

Et le p´tit machin qui repasse par là:

>>> iterateur = iter(MonIterable()) # Voilà ce que ça donne à la main
>>> iterateur.next()
2
>>> iterateur.next()
1
>>> iterateur.next() # l'exception: mécanisme naturel de Python pour stopper une boucle !
Traceback (most recent call last):
  File "<pyshell#9>", line 19, in <module>
    iterateur.next()
  File "<pyshell#9>", line 14, in next
    raise StopIteration()
StopIteration

Et qui fait marcher ce p´tit zinzinnnnnnnnnnnnn !

>>> iterateur = iter(range(3)) # c'est pareil pour tous les iterables
>>> iterateur.next() # un iterateur est juste un truc avec une méthode next()
0
>>> iterateur.next() # next() doit retourner la prochain valeur de l'iterable
1
>>> iterateur.next() # un itérateur itère donc sur un iterable
2
>>> iterateur.next() # jusqu'à la fin, où il lève StopIteratino
Traceback (most recent call last):
  File "<ipython-input-19-6c9f9efdd35c>", line 1, in <module>
    iterateur.next()
StopIteration

Ah bon ?

Pendant ce temps, à Vera Cruz

lundi 31 décembre 2012 à 00:36
[18:48:36 CEST][18:48:36 CEST] Max: mouiiii bonsoir
[18:48:59 CEST] Max: j'ai une petite question de sorting en python
 pour homme poilu intelligent :)
[18:49:12 CEST] Sam: je ne suis pas un homme
[18:49:25 CEST] Max: un surhomme ?
[18:49:33 CEST] Max: un post ado ?
[18:49:38 CEST] Max: un pre-homme
[18:49:46 CEST] Sam: je suis un bot poilu et un intelligent
[18:49:56 CEST] Max: c dur la vie
[18:50:09 CEST] Max: ha sinon t à partir de quand sur [censored]
que je vienne squater pour mes yeux ? :)
[18:53:44 CEST] Sam: mardi je pense
[18:53:46 CEST] Sam: à confirmer
[18:55:13 CEST] Max: ha ok, tu me donneras une fourchette où je
pourrais venir ;o)
[18:55:34 CEST] Sam: tu veux dormir dans une fourchette ?
[18:55:46 CEST] Max: ouais suis devenu indouiste
[18:56:05 CEST] Max: je passe ma troisieme dan de fakir
[18:57:00 CEST] Sam: moi je vais passer à mon troisième verre
de vrai kir
[18:57:03 CEST] Sam: chacun son truc
[19:00:21 CEST] Max: :)
[19:00:29 CEST] Max: in vino veritas
[19:00:35 CEST] Max: c là où tu trouveras la voix
[19:00:45 CEST] Sam: Bonum vinum laetificat cor hominis
[19:01:12 CEST] Max: tu vas pouvoir rentrer dans le cercle très
fermé de cortex
[19:01:23 CEST] Sam: très serré tu veux dire ?
[19:01:38 CEST] Max: esprit mal placé
[19:02:25 CEST] Sam: c'est le kir, ça me déplace les spris
[19:02:47 CEST] Sam: bon c'est quoi ta question python ?
[19:03:11 CEST] Max: ben c bon en fait, je testais en te la posant
parceque je pensais pas que ça marcherait
[19:03:16 CEST] Sam: lol
[19:03:21 CEST] Max: ct pour ordonné sur une clef une liste d'objet
[19:03:28 CEST] Sam: tu vas rire, mon samsung charge plus
[19:03:32 CEST] Max: enfin je pense que ça marche :)
[19:03:41 CEST] Max: quelle camelotte... encore le chargeur ?
[19:03:56 CEST] Sam: sorted(iterable, [key=]) ou iterable.sort() ?
[19:04:04 CEST] Sam: je sais pas, je pleur c'est tout
[19:04:06 CEST] Max: sorted(iterable, [key=])
[19:04:16 CEST] Max: en gros ça donne ça :
[19:04:17 CEST] Max:
related_search = SearchQuerySet()\
                      .filter(content=AutoQuery(related_query),
                              blur_coef__gt=60,
                              duration__gt=300,
                              rating__gt=50)[:500]

sorted(related_search, key=attrgetter('score'),
                                    reverse=True)
[19:04:25 CEST] Sam: parfait
[19:04:34 CEST] Sam: ça mérite une article sur sorted ça
[19:04:42 CEST] Max: le score c un truc retourné par solr,
pas un champs indexé par haystack
[19:04:45 CEST] Sam: le côté "key" est souvent mal compris
[19:04:54 CEST] Max: ouais enfin c bon là à vue de nez ?
[19:05:04 CEST] Max: car moi je pensais que ct sur une liste banale
[19:05:06 CEST] Sam: ouai niquel
[19:05:20 CEST] Sam: tu nous articlise tout ça ?
[19:05:29 CEST] Sam: comment utilise une clé sur "sorted" ?
[19:05:33 CEST] Max: là dans ma liste j'ai des objets search qui
ressemblent à des queryset et dans chaque objet un attribut score
[19:05:39 CEST] Sam: sinon j'ai toujours pas reçu [censored]
 de foxmask
[19:05:46 CEST] Max: pas glop
[19:05:55 CEST] Sam: faudrait peut être regarder le numéro de
livraison sur le site d'UPS
[19:05:59 CEST] Max: je connais que ce cas c un peu con
pour sorted :/
[19:06:21 CEST] Max: je vais pas passer en revue plusieurs cas de
figure ça fera pas un vrai article
[19:06:25 CEST] Sam: y a pas d'order by sur SearchQuerySet pour
 order by score ?
[19:06:42 CEST] Max: ben le order_by se fait sur les champs
 du searchindex
[19:06:48 CEST] Max: pas sur ce que retourne solr
[19:06:52 CEST] Sam: ok
[19:06:56 CEST] Max: là le score est retourné par solr
[19:07:30 CEST] Max: y a un more_like_this sur le
nouveau haystack mais
pas reussi à le faire marcher
[19:07:49 CEST] Max: faudrait lire 1 ou 2 kilometres de
doc en plus
[19:08:41 CEST] Sam: pour sortedy a que ce cas
[19:08:47 CEST] Sam: iterable, et optionellement key
[19:11:38 CEST] Sam: faudrait tentouiller elastic search
un de ces 4

Mettez vos sites Web et apps en plein écran avec l’API HTML 5 fullscreen

dimanche 30 décembre 2012 à 05:40

Il est loin le temps où le JS était un sous langage utilisé uniquement par des deumeurés en mal de <blink>. Maintenant c’est un sous langage utilisé par des gens très sérieux. PHP l’a bien prouvé, on peut être parfaitement utile en ayant une syntaxe daubée, et Javascript se pare donc de tout un tas de trucs surpuissants en ces temps de HTML5, CSS3 et Rambo8.

Fini donc le temps où votre site restait prisonnier de son canvas en 800×600, maintenant votre dernière application de calcul de budget de croquettes pour hérisson peut enfin s’exprimer dans toute la hauteur et la largeur d’un écran Retanal grâce à requestFullscreen(), qui va vous permettre … d’appuyer sur F11 à la place de l’utilisateur.

Mais il aura à faire un clic de confirmation quand même. Car il aura un gros prompt bien alarmant avant. Le progrès je vous dis !

Le code, en 2 / 2

Parce que bien entendu c’est incompatible avec les tondeuses à gazons et que selon les marques de votre pétrolettes, le prefix ne sera pas le même, on va commencer par vérifier le support du bouzin.

function supportFullScreen(){
    var doc = document.documentElement;
    return ('requestFullscreen' in doc) || ('mozRequestFullScreen' in doc && document.mozFullScreenEnabled) || ('webkitRequestFullScreen' in doc);
}

Puis on va demander à l’utilisateur si on a le droit, s’il-te-plait-pitié-déconne-pas-putain, de mettre votre site en plein écran. Car figurez vous qu’il y a un sérieux risque de phishing, comme en voici la démonstration rigolote.

function requestFullScreen(elem){
    if (elem.requestFullscreen) {
        elem.requestFullscreen();
    } else if (elem.mozRequestFullScreen) {
        elem.mozRequestFullScreen();
    } else if (elem.webkitRequestFullScreen) {
        elem.webkitRequestFullScreen();
    }
}

Ca s’utilise ainsi:

requestFullScreen(document.body)

A ce moment là l’écran de le navigateur passe en plein écran avec un gros panel d’avertissement genre “le certificat du site est invalide”, mais en gris. Et si l’utilisateur lit le message avant de se ruer sur le bouton “annuler” par réflexe, il s’apercevra qu’on lui explique ce qui se passe pour pas qu’il finisse de mourir de sa crise cardiaque.

Vous pouvez utiliser n’importe quel autre élément que body, et donc mettre en plein écran de tout petit widgets. Attention cependant, il va prendre la taille de tout l’écran, déclenchant tout un tas d’events de changement de taille, de display, changeant les overflows et gagnant un style par défaut qui sera différent selon le navigateur. Le W3C a un deal avec les browser vendors pour s’assurer que les devs Web ne soient jamais prêt d’être au chômage.

Passons.

Le problème quand on demande, c’est qu’on peut se voir répondre “non”. Donc en prime, il vaut mieux se concocter un fallback si jamais mémé s’affole devant la menace de voir ses onglets avalés par le viewport. Ici, je vous pond ça en jQuery, parceque la gestion des events à la main, c’est juste relou.

function onFullScreenEvent(callback){
    $(document).live("fullscreenchange mozfullscreenchange webkitfullscreenchange", function(){
        callback(document.fullscreen || document.mozFullScreen || document.webkitIsFullScreen);
    });
}

Bon, normalement on utilise ‘on()’ maintenant chez les gens branchés, mais j’arrive à le faire marche une fois sur deux. Donc ça s’utilise comme ça:

onFullScreenEvent(function(isFullscreen){
    faire un truc selon que l'on est en fullscreen ou pas, comme pousser mémé dans les orties
});

Clusions ensemble entre cons

Et voilà, vous avez une application fullscreenable. Il ne vous reste plus rien à faire. A part rajouter un bouton pour le permettre à l’utilisateur de mettre en fullscreen. Puis un autre pour sortir du fullscreen. Puis de gérer la transition des états de votre app. Les différences de rendu selon la taille des éléments et du style par défaut appliqué par le navigateur. Et bien entendu le plus dur: faire comprendre tout ce bordel à votre utilisateur final.

Plus rien à faire je vous dis: on vous a mâché le travail.

Si vous avez pas le temps, vous pouvez aussi mettre alert('appuyez sur F11 pour passer en plein écran'). Mais bon, j’écrirais des articles sur quoi moi après ?