Vue d'ensemble

LiveScript est un langage qui se compile en JavaScript. Il a un rapport direct avec JavaScript, et vous permet d'écrire du JavaScript de façon considérables sans répétitivité. LiveScript ajoute non seulement des fonctionnalités pour écrire du code fonctionnel, mais possède aussi nombre d'améliorations pour la programmation orientée objet et la programmation impérative.

LiveScript est un descendant indirect de CoffeeScript, direct de Coco avec beaucoup plus de compatibilité.

1.3.1:

zip
tar.gz
View project on GitHub

pour vous tenir au courant.

Dernier article: Powerful jQuery with LiveScript (EN)

Double-cliquez un exemple pour le compiler.

Examples

Fonction currifiée, switch implicite, opérateur de concaténation, et composition de fonctions :

LiveScript
take = (n, [x, ...xs]:list) -->
  | n <= 0     => []
  | empty list => []
  | otherwise  => [x] ++ take n - 1, xs









take 2, [1 2 3 4 5] #=> [1, 2]

take-three = take 3
take-three [3 to 8] #=> [3, 4, 5]

# Composition, 'reverse' de prelude.ls
last-three = reverse >> take-three >> reverse
last-three [1 to 8] #=> [6, 7, 8]
JavaScript
var take, takeThree, lastThree, slice$ = [].slice;
take = curry$(function(n, list){
  var x, xs;
  x = list[0], xs = slice$.call(list, 1);
  switch (false) {
  case !(n <= 0):
    return [];
  case !empty(list):
    return [];
  default:
    return [x].concat(take(n - 1, xs));
  }
});
take(2, [1, 2, 3, 4, 5]);

takeThree = take(3);
takeThree([3, 4, 5, 6, 7, 8]);

lastThree = compose$(reverse, takeThree, reverse);
lastThree([1, 2, 3, 4, 5, 6, 7, 8]);
function curry$(f, bound){
  var context,
  _curry = function(args) {
    return f.length > 1 ? function(){
      var params = args ? args.concat() : [];
      context = bound ? context || this : this;
      return params.push.apply(params, arguments) <
          f.length && arguments.length ?
        _curry.call(context, params) : f.apply(context, params);
    } : f;
  };
  return _curry();
}
function compose$() {
  var functions = arguments;
  return function() {
    var i, result;
    result = functions[0].apply(this, arguments);
    for (i = 1; i < functions.length; ++i) {
      result = functions[i](result);
    }
    return result;
  };
}
LiveScript
# Listage facile, objets implicites
table1 =
  * id: 1
    name: 'george'
  * id: 2
    name: 'mike'
  * id: 3
    name: 'donald'





table2 =
  * id: 2
    age: 21
  * id: 1
    age: 20
  * id: 3
    age: 26




# Accès implicite, accessignment
up-case-name = (.name .= to-upper-case!)






# Compréhensions, déstructuration, piping
[{id:id1, name, age} for {id:id1, name} in table1
                     for {id:id2, age} in table2
                     when id1 is id2]
|> sort-by (.id) # 'sort-by' de prelude.ls
|> each up-case-name # 'each' de prelude.ls
|> JSON.stringify
#=>
#[{"id":1,"name":"GEORGE","age":20},
# {"id":2,"name":"MIKE",  "age":21},
# {"id":3,"name":"DONALD","age":26}]











# Opérateurs en tant que fonctions, pipe inversée
map (.age), table2 |> fold1 (+)
#=> 67 ('fold1' et 'map' de prelude.ls)
JavaScript
var table1, table2, upCaseName, id1, name, id2, age;
table1 = [
  {
    id: 1,
    name: 'george'
  }, {
    id: 2,
    name: 'mike'
  }, {
    id: 3,
    name: 'donald'
  }
];
table2 = [
  {
    id: 2,
    age: 21
  }, {
    id: 1,
    age: 20
  }, {
    id: 3,
    age: 26
  }
];
upCaseName = function(it){
  return it.name = it.name.toUpperCase();
};
JSON.stringify(
each(upCaseName)(
sortBy(function(it){
  return it.id;
})(
(function(){
  var i$, ref$, len$, ref1$, j$, len1$, ref2$, results$ = [];
  for (i$ = 0, len$ = (ref$ = table1).length; i$ < len$; ++i$) {
    ref1$ = ref$[i$], id1 = ref1$.id, name = ref1$.name;
    for (j$ = 0, len1$ = (ref1$ = table2).length; j$ < len1$; ++j$) {
      ref2$ = ref1$[j$], id2 = ref2$.id, age = ref2$.age;
      if (id1 === id2) {
        results$.push({
          id: id1,
          name: name,
          age: age
        });
      }
    }
  }
  return results$;
}()))));
fold1(curry$(function(x$, y$){
  return x$ + y$;
}))(
map(function(it){
  return it.age;
}, table2));
function curry$(f, bound){
  var context,
  _curry = function(args) {
    return f.length > 1 ? function(){
      var params = args ? args.concat() : [];
      context = bound ? context || this : this;
      return params.push.apply(params, arguments) <
          f.length && arguments.length ?
        _curry.call(context, params) : f.apply(context, params);
    } : f;
  };
  return _curry();
}

Rappels désimbriqués (backcalls) , chaînage sans parenthèses :

LiveScript
<- $ 'h1' .on 'click'
alert 'boom!'
JavaScript
$('h1').on('click', function(){
  return alert('boom!');
});

Installation

Vous pouvez installer LiveScript depuis Node Package Manager (EN): sudo npm install -g LiveScript.

Vous pouvez aussi le télécharger (zip, tar.gz), ouvrir le dossier, et éxecuter sudo bin/slake install. En utilisant Git pour télécharger : git clone git://github.com/gkz/LiveScript.git && cd LiveScript && sudo bin/slake install. Node.js doit être installé sur votre machine.

Vous pouvez aussi l'inclure directement dans le navigateur en incluant le fichier LiveScript/extras/livescript.js via une balise script. C'est en fait la technique utilisée sur cette page. Si vous utilisez cette méthode, vos scripts LiveScript doivent être à même la page (sans utiliser l'attribut src), être placés après l'inclusion de livescript.js, et la balise script doit avoir l'attribut type="text/ls".

Utilisation

Utilisation: lsc [options] [fichiers] [arguments]

Options

-i, --interactivelance le compileur intéractif, utilisez ^J pour du multiligne
-c, --compilecompile en JavaScript et sauvegarde en .js
-d, --preludeimporte automatiquement prelude.ls (dans le REPL)
-k, --constcompile toutes les variables en tant que constantes
-o, --output DIRcompile dans le dossier de sortie précisé
-w, --watchsurveille les scripts lors de changements et relance
-s, --stdinlis le flux stdin
-e, --evallis les arguments CLI comme script
-r, --require FILE+inclus des bibliothèques avant l'éxecution
-b, --barecompile sans l'IIFE globale
-p, --printaffiche le résultat dans stdout
-l, --lexaffiche les tokens que le lexer produit
-t, --tokensaffiche les tokens que le rewriter produit
-a, --astaffiche l'arbre syntaxique abstrait que le parser produit
-j, --jsonaffiche/compile en tant que JSON
-n, --nodejs ARGS+passe les options à nodejs
-v, --versionaffiche la version
-h, --helpaffiche ceci

Exemples

  • Lance un fichier LiveScript (via node.js): lsc file.ls — le .ls est optionnel.
  • Compile un fichier LiveScript, créant un fichier .js : lsc -c file.ls
  • Surveille un fichier LiveScript et le compile lors de changements : lsc -wc file.ls
  • Compile un dossier entier vers un autre dossier : lsc -co output src
  • Surveille un dossier, compile les fichiers vers un autre dossier : lsc -wco output src
  • Compile un petit extrait et affiche le résultat dans la console : lsc -bpe '[1 to 5]'
  • Lance le compilateur intéractif : lscCtrl-D pour quitter, utilisez Ctrl-J pour du multiligne.

Éditeurs de texte

Ajoutez le votre dans le wiki (EN).

Bibliothèque standard

prelude.ls est la bibliothèque recommandée pour utiliser avec LiveScript. Elle vous permet de faire des choses telles que :

[1 2 3] |> map (* 2) |> filter (> 3) |> fold1 (+)
#=> 10

Vous pouvez importer automatiquement prelude.ls via l'option -d (ou --prelude) du compilateur.

Vous pouvez tester tous ces exemples en utilisant le compilateur sur la droite.

Pour nettoyer votre code, vous pouvez omettre les parenthèses quand vous appelez une fonction.

add 2, 3
add(2, 3);

Les commentaires sont :

# d'ici à la fin de la ligne.
// d'ici à la fin de la ligne.

Amis de Lisp, vous serez heureux d'apprendre que vous pouvez utiliser les tirets dans vos noms de variables et de fonctions. Ces noms sont identiques à — et compilés vers — leur équivalent camelCase. my-value = 42 == myValue = 42.

L'extension est .ls.

Définir une fonction

Définir une fonction est très simple en LiveScript :


(x, y) -> x + y



-> # une fonction vide

times = (x, y) ->
  x * y
# multiligne, assignée à une variable
# comme en JavaScript
var times;
(function(x, y){
  return x + y;
});

(function(){});

times = function(x, y){
  return x * y;
};


Comme vous pouvez le voir, les déclarations sont bien plus courtes. Vous pouvez aussi voir que le return est implicite. En LiveScript, presque tout est une expression et la dernière est automatiquement retournée. Cependant, vous pouvez toujours utiliser return pour forcer un retour, ou ajouter un point d'exclamation à la définition pour supprimer le retour implicite no-ret = !(x) -> ....

Attribution

L'attribution basique est tout simplement variable = value, et il n'y a pas besoin de déclarer les variables. Cependant, contrairement à CoffeeScript, vous devez utiliser := pour modifier les variables dans des portées supérieures.


x = 10


do ->
  x = 5

x #=> 10

do ->
  x := 2

x #=> 2
var x;
x = 10;

(function(){
  var x;
  return x = 5;
})();
x;

(function(){
  return x = 2;
})();
x;

Tout (ou presque) étant une expression, vos attributions peuvent être complexes.

x = if 2 + 2 == 4
    then 10
    else 0
x #=> 10
var x;
x = 2 + 2 === 4 ? 10 : 0;

x;

Même les boucles, switch, voire les try/catch sont des expressions.

Si vous voulez simplement déclarer une variable sans l'initialiser, utilisez var.

var x
var x;

Vous pouvez aussi déclarer des constantes avec le mot-clef const.

Les vérifications sont faites à la compilation — le JavaScript compilé est le même.

Essayer de compiler le code suivant :

const x = 10
x = 0

Donnera l'erreur suivante : redeclaration of constant "x" on line 2 (re-déclaration de la constante "x" ligne 2).

À l'inverse, les objets ne sont pas "gêlés" — vous pouvez quand même modifier leurs propriétés. Vous pouvez forcer toutes les variables à être des constantes à l'aide de l'option -k (ou --const).

Infos

Pour voir les différences avec CoffeeScript, allez au paragraphe Conversion depuis CoffeeScript.

Vous pouvez double-cliquer n'importe quel exemple pour le charger dans le compilateur à droite, ou vous pouvez charger votre propre code. Notez que LiveScript entoure le JavaScript dans une IIFE (function(){...contenu...}).call(this); — retiré des exemples et des résultats du compilateur pour la concision.

Littéraux

Nombres

Les nombres littéraux existent comme dans les autres langages. Attention cependant, .4 est invalide, vous devez le précéder d'un zero : 0.4.

42
17.34
0.4
42;
17.34;
0.4;

Les traits de soulignement (underscores) et autres lettres sont ignorées :

64_000km
64000;

Vous pouvez utiliser n'importe quelle base de 2 à 36 en utilisant ~ :

6~12
2~1000
16~ff
8;
8;
255;

Booléens, Void, Null

Alias comme en CoffeeScript :

true
false
on
off
yes
no
true;
false;
true;
false;
true;
false;

En JavaScript, undefined peut être redéfini, il est donc plus prudent d'utiliser void qui produira toujours undefined.

Un void seul n'est pas inclus dans la compilation (en tant que placeholder) — il peut cependant être utilisé comme valeur :


void
x = void

null
var x;
// void compiles to nothing here!
x = void 8;

null;

L'utilisation de undefined comme alias pour void est déprécié et sera retiré dans la prochaine version de LiveScript. Utilisez void à la place.

Chaînes

Vous pouvez utiliser des apostrophes ou des guillemets :

'a string'
"a string"
'a string';
"a string";

Les chaînes sans espaces peuvent être écrites précédées d'un anti-slash :

\word
'word';

Les chaînes entre guillemets acceptent les interpolations. Les variables simples peuvent être interpolées sans accolades.


"The answer is #{2 + 2}"
'As #{is}'

variable = "world"
"Hello #variable"
var variable;
"The answer is " + (2 + 2);
'As #{is}';

variable = "world";
"Hello " + variable;

Préfixer une interpolation avec % retourne les parties en tant que liste, vous permettant de les joindre comme bon vous semble.

%"#x #y"
[x, " ", y];

Avec des chaînes multilignes (vous pouvez aussi utiliser des guillemets pour utiliser les interpolations) :

multiline = 'les chaînes peuvent être multilignes \
            en continuant tant que nécessaire \
            les espaces précédant sont \
            ignorés'
heredoc = '''
            les chaînes peuvent être multilignes
            en continuant tant que nécessaire
            les espaces précédant sont
            ignorés
'''
nospace = 'deadbeef
           deadbeef'
var multiline, heredoc, nospace;
multiline = 'les chaînes peuvent être multilignes en continuant tant que nécessaire les espaces précédant sont ignorés';

heredoc = 'les chaînes peuvent être multilignes\nen continuant tant que nécessaire\nles espaces précédant sont\nignorés';




nospace = 'deadbeefdeadbeef';

Commentaires

Les commentaires monoligne commencent avec un #. Ils sont supprimés à la compilation.

# commentaire


Les commentaires multiligne sont gardés

/* Utilisez ce format
   pour conserver
   vos commentaires
*/
/* Utilisez ce format
   pour conserver
   vos commentaires
*/

Objets

Les accolades sont optionnelles :


obj = {prop: 1, thing: 'moo'}



person =
  age:      23
  eye-color: \green
  height:   180cm

oneline = color: \blue, heat: 4
var obj, person, oneline;
obj = {
  prop: 1,
  thing: 'moo'
};
person = {
  age: 23,
  eyeColor: 'green',
  height: 180
};
oneline = {
  color: 'blue',
  heat: 4
};

Les clefs peuvent être dynamiques :

obj =
  "#variable": 234
  (person.eye-color): false
var obj, ref$;
obj = (ref$ = {}, ref$[variable + ""] = 234, ref$[person.eyeColor] = false, ref$);

Raccourci pour les propriétés — vous permet d'utiliser plus rapidement des variables quand la clef est le nom de la variable en valeur :

x = 1
y = 2
obj = {x, y}
var x, y, obj;
x = 1;
y = 2;
obj = {
  x: x,
  y: y
};

Raccourci pour les booléens :

{+debug, -live}
({
  debug: true,
  live: false
});

This — pas besoin d'utiliser de . pour accéder aux propriétés.

this
@
@location
this;
this;
this.location;

Expressions régulières

Les expressions régulières sont délimitées avec un simple /.

/moo/gi
/moo/gi;

Lorsque délimitées avec //, vous pouvez utiliser des espaces, des interpolations et les écrire sur plusieurs lignes

//
| [!=]==?             # équalité
| @@                  # constructor
| <\[(?:[\s\S]*?\]>)? # mots
//g
/|[!=]==?|@@|<\[(?:[\s\S]*?\]>)?/g;





Listes

Les listes simples sont entre crochets :

[1, person.age, 'French Fries']
[1, person.age, 'French Fries'];

Les virgules ne sont pas nécessaires si l'élément précédent n'est pas appellable :

[1 2 3 true void \word 'hello there']
[1, 2, 3, true, void 8, 'word', 'hello there'];

Vous pouvez créer des listes implicites en indentant un bloc. Vous devez avoir au moins deux éléments pour que cela fonctionne. Si vous n'en avez qu'un, utilisez le yaddayaddayadda ... :

my-list =
  32 + 1
  person.height
  \beautiful

one-item =
  1
  ...
var myList, oneItem;
myList = [32 + 1, person.height, 'beautiful'];



oneItem = [1];



Quand vous utilisez des listes implicites, vous pouvez utiliser un astérisque * pour désambiguïser entre les structures.

L'astérique ne représente pas un élément de la liste, mais indente en fait une structure implicite pour ne pas qu'elle soit mélangée avec les autres.

tree =
  * 1
    * 2
      3
    4
  * 5
    6
    * 7
      8
      * 9
        10
    11

obj-list =
  * name: \tessa
    age:  23
  * name: \kendall
    age:  19


obj =
  * name: \tessa
    age:  23

obj-one-list =
  * name: \tessa
    age:  23
  ...
var tree, objList, obj, objOneList;
tree = [[1, [2, 3], 4], [5, 6, [7, 8, [9, 10]], 11]];









objList = [
  {
    name: 'tessa',
    age: 23
  }, {
    name: 'kendall',
    age: 19
  }
];
obj = {
  name: 'tessa',
  age: 23
};
objOneList = [{
  name: 'tessa',
  age: 23
}];

Liste de mots :

<[ list of words ]>
['list', 'of', 'words'];

Séries

Dans une série, to signifie à, et inclus le nombre à droite. til signifie jusqu'à et n'inclus pas le nombre.

Vous pouvez optionnellement ajouter un by qui définira l'étape.

Si vous omettez le premier nombre, il sera 0.

Avec des nombres/chaînes littéraux :

[1 to 5]       #=> [1, 2, 3, 4, 5]
[1 til 5]      #=> [1, 2, 3, 4]
[1 to 10 by 2] #=> [1, 3, 7, 9]
[4 to 1]       #=> [4, 3, 2, 1]
[to 5]         #=> [0, 1, 2, 3, 4, 5]
[\A to \D]     #=> ['A', 'B', 'C', D']
[1, 2, 3, 4, 5];
[1, 2, 3, 4];
[1, 3, 5, 7, 9];
[4, 3, 2, 1];
[0, 1, 2, 3, 4, 5];
["A", "B", "C", "D"];

Avec n'importe quelle expression — si vous voulez aller vers le bas (d'un nombre à un autre plus petit), vous devez le préciser explicitement avec by -1.

x = 4
[1 to x]       #=> [1, 2, 3, 4]
[x to 0 by -1] #=> [4, 3, 2, 1, 0]
var x, i$;
x = 4;
for (i$ = 1; i$ <= x; ++i$) {
  i$;
}
for (i$ = x; i$ >= 0; --i$) {
  i$;
}

Autres

Labels (utile pour les boucles imbriquées ainsi qu'avec les blocs comme let) :

:label 4 + 2
label: {
  4 + 2;
}

Raccourci pour constructor :

@@
@@x
x@@y
constructor;
constructor.x;
x.constructor.y;

Yaddayaddayadda — un espace réservé :

...
throw Error('unimplemented');

Opérateurs

Nombre

Les opérateurs mathématiques standarts :

1 + 2 #=> 3
3 - 4 #=> -1
6 * 2 #=> 12
8 / 4 #=> 2
1 + 2;
3 - 4;
6 * 2;
8 / 4;

Il y a aussi un opérateur pour obtenir le reste d'une division, comme en JavaScript — ainsi que le modulo :


-3 % 4  #=> -3
-3 %% 4 #=> 1
var ref$;
-3 % 4;
((-3) % (ref$ = 4) + ref$) % ref$;

L'opérateur de puissance est associatif à droite, et a une plus haute précédence que les opérateurs unaires. ^ est un alias pour ** :

2 ** 4     #=> 16
3 ^ 4      #=> 81
-2 ^ 2 ^ 3 #=> -256
Math.pow(2, 4);
Math.pow(3, 4);
-Math.pow(2, Math.pow(2, 3));

Incrémentation, décrementation :


n = 0
n++ #=> 0
++n #=> 2
n-- #=> 2
--n #=> 0
x = n++ #=> 0
x #=> 0
n #=> 1
x = ++n #=> 2
x #=> 2
n #=> 2
var n, x;
n = 0;
n++;
++n;
n--;
--n;
x = n++;
x;
n;
x = ++n;
x;
n;

Opérateurs de bits et de décalage :

14 .&. 9   #=> 8
14 .|. 9   #=> 15
14 .^. 9   #=> 7
~9         #=> -10
9  .<<. 2  #=> 36
-9 .>>. 2  #=> -3
-9 .>>>. 2 #=> 1073741821
14 & 9;
14 | 9;
14 ^ 9;
~9;
9 << 2;
-9 >> 2;
-9 >>> 2;

Conversion vers un nombre :

+'4' #=>  4
-'3' #=> -3
+'4';
-'3';

Comparaison

Égalité stricte (pas de conversion de type) :

2 + 4 == 6      #=> true
\boom is 'boom' #=> true

\boom != null   #=> true
2 + 2 is not 4  #=> false
0 + 1 isnt 1    #=> false
2 + 4 === 6;
'boom' === 'boom';

'boom' !== null;
2 + 2 !== 4;
0 + 1 !== 1;

Égalité simple (avec conversion de type) :

2 ~= '2'       #=> true
\1 !~= 1       #=> false
2 == '2';
'1' != 1;

Supérieur/inférieur à :

2 < 4           #=> true
9 > 7           #=> true
8 <= 8          #=> true
7 >= 8          #=> false
2 < 4;
9 > 7;
8 <= 8;
7 >= 8;

Enchaînement de comparaisons :


1 < 2 < 4        #=> true
1 < 2 == 4/2 > 0 #=> true
var ref$;
1 < 2 && 2 < 4;
1 < 2 && 2 === (ref$ = 4 / 2) && ref$ > 0;

Minimum/maximum — retourne le minimum/maximum des deux opérandes :


4 >? 8     #=> 8
9 - 5 <? 6 #=> 4
var ref$;
4 > 8 ? 4 : 8;
(ref$ = 9 - 5) < 6 ? ref$ : 6;

Quand une des deux opérandes d'une comparaison (== ou is, ainsi que les versions inversées) est une expression régulière littérale, elle sera testée contre l'autre opérande.

Une égalité donne un appel d'exec pour que vous puissiez utiliser les résultats, alors qu'une inégalité utilise test pour les performances.

/^e(.*)/ is 'enter' #=> ["enter","nter"]
/^e(.*)/ == 'zx'    #=> null
/moo/ != 'loo'      #=> true
/^e(.*)/.exec('enter');
/^e(.*)/.exec('zx');
!/moo/.test('loo');

Logique

Les bases :

true and false #=> false
true && false  #=> false

true or false  #=> true
true || false  #=> true

not false      #=> true
!false         #=> true
true && false;
true && false;

true || false;
true || false;

!false;
!false;

Ou exclusif — un opérateur peu vu dans d'autres langages.

false xor true  #=> true
false xor false #=> false
1 xor 0         #=> 1
1 xor 1         #=> false
!false !== !true && (false || true);
!false !== !false && (false || false);
!1 !== !0 && (1 || 0);
!1 !== !1 && (1 || 1);

and, or, et xor ferment les appels implicites, contrairement à || et && :

even 0 and 3 #=> 3
even 0 &&  3 #=> true
even(0) && 3;
even(0 && 3);

Vous pouvez appeler les opérateurs logiques :

(f or g) 1
(f and g or h) 3 4
f(1) || g(1);
f(3, 4) && g(3, 4) || h(3, 4);

Vous n'êtes pas limités aux variables ici ! Vous pouvez utiliser les littéraux.

('a' || /b/ && f) c
'a' === c || /b/.exec(c) && f(c);

In/Of

Utilisez in pour savoir si un élément est dans une liste, et of pour savoir si l'objet contient une clef :


list = [7 8 9]
2 in [1 2 3 4 5]             #=> true
3 in list                    #=> false
\id of id: 23, name: \rogers #=> true
var list;
list = [7, 8, 9];
2 === 1 || 2 === 2 || 2 === 3 || 2 === 4 || 2 === 5;
in$(3, list);
'id' in {
  id: 23,
  name: 'rogers'
};
function in$(x, xs){
  var i = -1, l = xs.length >>> 0;
  while (++i < l) if (x === xs[i]) return true;
  return false;
}

Piping

À la place d'une série d'appels imbriqués, vous pouvez utiliser le piping. x |> f et f <| x sont équivalents à f(x).

[1 2 3] |> reverse |> head #=> 3


reverse <| [1 2 3]         #=> [3,2,1]
head(
reverse(
[1, 2, 3]));
reverse([1, 2, 3]);

Le piping est bien sûr multiligne, pour plus de clarté.

4
|> (+ 1)
|> even
#=> false
even(
(function(it){
  return it + 1;
})(
4));

Vous pouvez utiliser le piping directement après une expression, comme par exemple un for :

for a in <[fi bu ba]>
  "#{a}zz"
|> map (.toUpperCase!)
|> (* ', ')
var a, join$ = [].join;
(function(it){
  return join$.call(it, ', ');
})(
map(function(it){
  return it.toUpperCase();
})(
(function(){
  var i$, ref$, len$, results$ = [];
  for (i$ = 0, len$ = (ref$ = ['fi', 'bu', 'ba']).length; i$ < len$; ++i$) {
    a = ref$[i$];
    results$.push(a + "zz");
  }
  return results$;
}())));

Composition

La composition vous permet de créer des fonctions composées d'autres fonctions. LiveScript a deux opérateurs pour : avant (>>) et arrière (<<).

(f << g) x est pareil que f(g(x)), et (f >> g) x est équivalent à g(f(x)).


odd     = (not) << even
odd 3   #=> true
var odd;
odd = compose$(even, not$);
odd(3);
function compose$() {
  var functions = arguments;
  return function() {
    var i, result;
    result = functions[0].apply(this, arguments);
    for (i = 1; i < functions.length; ++i) {
      result = functions[i](result);
    }
    return result;
  };
}
function not$(x){ return !x; }

Pour être un peu plus clair à propos des différences entre les deux opérateurs :

add-two-times-two = (+ 2) >> (* 2)
times-two-add-two = (+ 2) << (* 2)

add-two-times-two 3 #=> (3+2)*2 = 10
times-two-add-two 3 #=> (3*2)+2 = 8
var addTwoTimesTwo, timesTwoAddTwo;
addTwoTimesTwo = compose$((function(it){
  return it + 2;
}), (function(it){
  return it * 2;
}));
timesTwoAddTwo = compose$((function(it){
  return it * 2;
}), (function(it){
  return it + 2;
}));
addTwoTimesTwo(3);
timesTwoAddTwo(3);
function compose$() {
  var functions = arguments;
  return function() {
    var i, result;
    result = functions[0].apply(this, arguments);
    for (i = 1; i < functions.length; ++i) {
      result = functions[i](result);
    }
    return result;
  };
}

Vous pouvez aussi utiliser un point espacé comme alias pour <<,
par exemple f . g, comme en Haskell.

Liste

Vous pouvez concaténer deux listes ensemble : (attention, l'opérateur doit être doublement espacé)

<[ un deux trois ]> ++ [\quatre]
#=> ['un','deux','trois','quatre']
['un', 'deux', 'trois'].concat(['quatre']);


Répétition :

[\ha] * 3 #=> ['ha','ha','ha']
['ha', 'ha', 'ha'];

Jointure :

<[ one two three ]> * \|      #=> 'one|two|three'
['one', 'two', 'three'].join('|');

Propagation unaire — quand l'opérande est une liste littérale, l'opérateur unaire est appliqué à tous les éléments :

r = +...[4 5 6]          #=> [+4, +5, +6]
t = typeof! ...[\b 5 {}] #=> ["String", "Number", "Object"]
c = ~...[4, 5]           #=> [-5, -6]
++...player<[strength hp]>

# marche aussi avec -, --, typeof,
# ! et delete! voire -~-~
i = new ...[some, classes]
c = ^^...[copy, these, {}]
delete ...list[1, 2, 3]
do ...[a, b, c]
var r, t, c, i, toString$ = {}.toString;
r = [+4, +5, +6];
t = [toString$.call('b').slice(8, -1), toString$.call(5).slice(8, -1), toString$.call({}).slice(8, -1)];
c = [~4, ~5];
++player['strength'], ++player['hp'];
i = [new some, new classes];
c = [clone$(copy), clone$(these), clone$({})];
delete list[1], delete list[2], delete list[3];
a(), b(), c();
function clone$(it){
  function fun(){} fun.prototype = it;
  return new fun;
}

Chaîne

Concaténation :

'hello' + ' ' + 'world' #=> 'hello world'
string = 'say '         #=> 'say '
string += \yeah         #=> 'say yeah'
var string;
'hello' + ' ' + 'world';
string = 'say ';
string += 'yeah';

Répétition :

'X' * 3      #=> 'XXX'
'XXX';

Soustraction / division de chaînes — soustraction pour replace, division pour split.

'say yeah' - /h/ #=> 'say yea'
'say yeah' / \y  #=> ['sa',' ','eah']
'say yeah'.replace(/h/, '');
'say yeah'.split('y');

Existence/Inexistence

L'opérateur ? peut être utilisé dans de nombreux contextes pour vérifier une présence.

bigfoot ? 'grizzly bear'     #=> 'grizzly bear'
string = \boom if window?    #=> 'boom'
document?.host               #=> 'gkz.github.com'
var string;
(typeof bigfoot == 'undefined' || bigfoot === null) && 'grizzly bear';
if (typeof window != 'undefined' && window !== null) {
  string = 'boom';
}
if (typeof document != 'undefined' && document !== null) {
  document.host;
}

Objet

instanceof — les listes littérales sur la droite sont étendues (propagation unaire).


new Date() instanceof Date           #=> true
new Date() instanceof [Date, Object] #=> true
var ref$;
new Date() instanceof Date;
(ref$ = new Date()) instanceof Date || ref$ instanceof Object;

typeof — ainsi que la variante LiveScript avec un point d'exclamation :


typeof /^/  #=> object
typeof! /^/ #=> RegEx
var toString$ = {}.toString;
typeof /^/;
toString$.call(/^/).slice(8, -1);

delete retourne la valeur de l'élément supprimé :

obj = {one: 1, two: 2}
r = delete obj.one
r #=> 1
var obj, r, ref$;
obj = {
  one: 1,
  two: 2
};
r = (ref$ = obj.one, delete obj.one, ref$);
r;

delete! est l'équivalent de delete en JavaScript, et retourne false uniquement si la propriété existe mais ne peut être supprimée :

obj = {one: 1, two: 2}
delete! obj.one #=> true
delete! Math.PI #=> false
var obj;
obj = {
  one: 1,
  two: 2
};
delete obj.one;
delete Math.PI;

Copie de propriétés — de droite à gauche. <<< pour les propriétés propres (Object.hasOwnProperty), <<<< pour toutes les propriétés. import et import all sont des alias. Si vous omettez l'opérande gauche, this est implicite.

obj = {one: 1, two: 2}
obj <<< three: 3 #=> {one: 1, two: 2, three: 3}
{go: true} <<<< window
import obj
var obj;
obj = {
  one: 1,
  two: 2
};
obj.three = 3;
importAll$({
  go: true
}, window);
import$(this, obj);
function importAll$(obj, src){
  for (var key in src) obj[key] = src[key];
  return obj;
}
function import$(obj, src){
  var own = {}.hasOwnProperty;
  for (var key in src) if (own.call(src, key)) obj[key] = src[key];
  return obj;
}

Clonage — clone le prototype de l'opérande. Ne clone pas en profondeur, à la place, l'opérande sera le prototype de l'objet cloné.

Souvenez-vous que la sérialisation JSON ne montre pas les prototypes.

obj = {one: 1}


obj2 = ^^obj
obj2.two = 2
obj2 #=> {one: 1, two: 2}
# ci-dessus avec les propriétés du prototype
# la sérialisation JSON donnerait `{two: 2}`
obj  #=> {one: 1}
var obj, obj2;
obj = {
  one: 1
};
obj2 = clone$(obj);
obj2.two = 2;
obj2;


obj;
function clone$(it){
  function fun(){} fun.prototype = it;
  return new fun;
}

Le with infixe (ou cloneport) combine le clonage et la copie de propriétés. Il est équivalent à ^^obj <<< obj2

Souvenez-vous que le clonage est un clonage prototypal, et que la sérialisation JSON ne montre pas les prototypes.

girl = {name: \hanna, age: 22}
guy  = girl with name: \john
guy  #=> {name: 'john',  age: 22}
# ci-dessus avec les propriétés du prototype
# la sérialisation JSON donnerait `{name: 'john'}`
girl #=> {name: 'hanna', age: 22}
var girl, guy, ref$;
girl = {
  name: 'hanna',
  age: 22
};
guy = (ref$ = clone$(girl), ref$.name = 'john', ref$);
guy;
girl;
function clone$(it){
  function fun(){} fun.prototype = it;
  return new fun;
}

Application partielle, opérateurs en tant que fonctions

Vous pouvez "appliquer partiellement" les opérateurs pour les utiliser en tant que fonctions :

(+ 2) 4         #=> 6
(*) 4 3         #=> 12

(not) true      #=> false
(in [1 to 3]) 2 #=> true
(function(it){
  return it + 2;
})(4);
curry$(function(x$, y$){
  return x$ * y$;
})(4, 3);
not$(true);
(function(it){
  return it === 1 || it === 2 || it === 3;
})(2);
function curry$(f, bound){
  var context,
  _curry = function(args) {
    return f.length > 1 ? function(){
      var params = args ? args.concat() : [];
      context = bound ? context || this : this;
      return params.push.apply(params, arguments) <
          f.length && arguments.length ?
        _curry.call(context, params) : f.apply(context, params);
    } : f;
  };
  return _curry();
}
function not$(x){ return !x; }

require!

Avoir beaucoup de modules signifie beaucoup de redondance dans les inclusions. Avec require!, qui prend identifieur(s), chaîne, liste ou objet, vous pouvez éviter cette redondance.

Si vous voulez inclure un module avec des tirets, vous devez utiliser une chaîne.

Vous pouvez renommer ce que vous incluez en utilisant un objet littéral.


require! lib
require! 'lib1'

require! prelude-ls # no
require! 'prelude-ls'

require! [fs, path]
require! <[ fs path ]>



require! jQuery: $

require! {
  fs
  path
  lib4: lib
}
var lib, lib1, preludeLs, fs, path, $;
lib = require('lib');
lib1 = require('lib1');

preludeLs = require('preludeLs');
preludeLs = require('prelude-ls');

fs = require('fs');
path = require('path');
fs = require('fs');
path = require('path');

$ = require('jQuery');

fs = require('fs');
path = require('path');
lib = require('lib4');

Vous pouvez aussi inclure des parties de modules :

require! {
  lib
  lib:{part1}
}
var lib, part1;
lib = require('lib');
part1 = require('lib').part1;

Les noms de fichier sont extraits :


require! 'lib.js'
require! './dir/lib1.js'
var lib, lib1;
lib = require('lib.js');
lib1 = require('./dir/lib1.js');

Function

La définition de fonction est très simple en LiveScript :


(x, y) -> x + y

-> # une fonction vide

times = (x, y) ->
  x * y
# multiligne, assignée à une variable
# comme en JavaScript
var times;
(function(x, y){
  return x + y;
});
(function(){});

times = function(x, y){
  return x * y;
};

Comme vous pouvez le voir, les déclarations sont bien plus courtes. Vous pouvez aussi voir que le return est implicite. En LiveScript, presque tout est une expression et la dernière est automatiquement retournée. Cependant, vous pouvez toujours utiliser return pour forcer un retour, ou ajouter un point d'exclamation à la définition pour supprimer le retour implicite no-ret = (x) !-> ....

f = !-> 2
g = (x) !-> x + 2
var f, g;
f = function(){
  2;
};
g = function(x){
  x + 2;
};

Appel

vous pouvez omettre les parenthèses quand vous appelez une fonction, et comme pour les listes, vous pouvez omettre la virgule entre les arguments si l'argument précédent n'est pas appellable :


x = 4
Math.pow x, 3 #=> 64
Math.pow 2 3  #=> 8
var x;
x = 4;
Math.pow(x, 3);
Math.pow(2, 3);

Si vous appelez une fonction sans arguments, vous pouvez utiliser ! — vous n'avez pas besoin non plus de point pour enchaîner les fonctions ainsi appelées.

f!
[1 2 3].reverse!slice 1 #=> [2,1]
f();
[1, 2, 3].reverse().slice(1);

and, or, xor, un . espacé ou ?. ferment les appels implcites — permettant d'enchaîner les appels sans parenthèses.

$ 'h1' .find 'a' .text! #=> LiveScript
$('h1').find('a').text();

Vous pouvez utiliser do pour transformer un bloc de code en un appel (et appeler une fonction sans arguments) :

do -> 3 + 2 #=> 5
calc =
  five: do
    3 + 2
var calc;
(function(){
  return 3 + 2;
})();
calc = {
  five: 3 + 2
};

Si vous utilisez do sur une fonction nommée, et que le do n'est pas utilisé en tant qu'expression, l'appel appellera la fonction directement :

i = 0
f 9 #=> 9
i   #=> 1
do function f x
  ++i
  x
i   #=> 2
var i;
i = 0;
f(9);
i;
function f(x){
  ++i;
  return x;
} f();
i;

Utilisez do pour appeler une function avec un objet implicite :

func do
  a: b
  c: d
func({
  a: b,
  c: d
});

do sert en vérité à bien plus que ça - c'est une construction permettant de créer un Block et donc d'éviter les parenthèses :

pow do
  1
  2

h 1 do
  a: 2
  b: 5
pow(1, 2);


h(1, {
  a: 2,
  b: 5
});

Vous pouvez appeler des fonctions infixes avec `.

add = (x, y) -> x + y
3 `add` 4 #=> 7
var add;
add = function(x, y){
  return x + y;
};
add(3, 4);

Appeler une fonction avec l'opérateur reste (splat) ... lui passe les arguments de la fonction actuelle. C'est particulièrement utile avec super.

f = (x, y) ->
  x + y

g = (a, b) ->
  f ...

g 3 4 #=> 7
var f, g;
f = function(x, y){
  return x + y;
};
g = function(a, b){
  return f.apply(this, arguments);
};
g(3, 4);

Paramètres

Paramètres étendus :

set-person-params = (
  person # objet cible
  person.age
  person.height
) -> person

person = set-person-params {}, 21, 180cm
#=> {age: 21, height: 180}
var setPersonParams, person;
setPersonParams = function(person, age, height){
  person.age = age;
  person.height = height;
  return person;
};
person = setPersonParams({}, 21, 180);

Particulièrement utile avec this :

set-text = (@text) -> this
var setText;
setText = function(text){
  this.text = text;
  return this;
};

Vous pouvez donner des valeurs par défault aux arguments :

add = (x = 4, y = 3) -> x + y
add 1 2 #=> 3
add 1   #=> 4
add!    #=> 7
var add;
add = function(x, y){
  x == null && (x = 4);
  y == null && (y = 3);
  return x + y;
};
add(1, 2);
add(1);
add();

... ou utiliser n'importe quel opérateur logique (dans les paramètres, x = 2 correspond à x ? 2):

add = (x && 4, y || 3) -> x + y
add 1 2 #=> 6
add 2 0 #=> 7
var add;
add = function(x, y){
  x && (x = 4);
  y || (y = 3);
  return x + y;
};
add(1, 2);
add(2, 0);

Vous pouvez aussi déstructurer les arguments :

set-cords = ({x, y}) -> "#x,#y"
set-cords y: 2, x: 3 #=> '3,2'
var setCords;
setCords = function(arg$){
  var x, y;
  x = arg$.x, y = arg$.y;
  return x + "," + y;
};
setCords({
  y: 2,
  x: 3
});

... et aller jusqu'à donner des valeurs par défaults, ou utiliser n'importe quel opérateur logique dans ces paramètres déstructurés :

set-cords = ({x = 1, y = 3} = {}) -> "#x,#y"
set-cords y: 2, x: 3 #=> '3,2'
set-cords x: 2       #=> '2,3'
set-cords y: 7       #=> '1,7'
set-cords!           #=> '1,3'
var setCords;
setCords = function(arg$){
  var ref$, x, ref1$, y;
  ref$ = arg$ != null
    ? arg$
    : {}, x = (ref1$ = ref$.x) != null ? ref1$ : 1, y = (ref1$ = ref$.y) != null ? ref1$ : 3;
  return x + "," + y;
};
setCords({
  y: 2,
  x: 3
});
setCords({
  x: 2
});
setCords({
  y: 7
});
setCords();

Vous pouvez aussi obtenir le reste (splat) des paramètres :

f = (x, ...ys) -> x + ys.1
f 1 2 3 4 #=> 4
var f;
f = function(x){
  var ys, res$, i$, to$;
  res$ = [];
  for (i$ = 1, to$ = arguments.length; i$ < to$; ++i$) {
    res$.push(arguments[i$]);
  }
  ys = res$;
  return x + ys[1];
};
f(1, 2, 3, 4);

Currification

Les fonctions currifiées sont très puissantes. Pour faire simple, les fonctions appelées avec moins d'arguments qu'elles n'en ont retournent une fonction partiellement appliquée qui acceptera le reste des arguments. En LiveScript, ces fonctions sont définiés avec une longue flèche :

times = (x, y) --> x * y
times 2, 3       #=> 6 (fonctionnement normal)
double = times 2
double 5         #=> 10
var times, double;
times = curry$(function(x, y){
  return x * y;
});
times(2, 3);
double = times(2);
double(5);
function curry$(f, bound){
  var context,
  _curry = function(args) {
    return f.length > 1 ? function(){
      var params = args ? args.concat() : [];
      context = bound ? context || this : this;
      return params.push.apply(params, arguments) <
          f.length && arguments.length ?
        _curry.call(context, params) : f.apply(context, params);
    } : f;
  };
  return _curry();
}

Vous pouvez définir des fonctions liées avec une longue flèche ondulée : ~~>

Fonctions nommées

Vous pouvez créer des fonctions nommées, utilisables n'importe où dans le fichier même si définies à la fin. Ces fonctions sont constantes et ne peuvent être redéfinies.

LiveScript ne touche pas au bug JScript (EN).

util!  #=> 'disponible avant déclaration'
util2! #=> 2

function util
  'disponible avant déclaration'
function util2 then 2
util();
util2();
function util(){
  return 'disponible avant déclaration';
}
function util2(){
  return 2;
}

Vous pouvez lier la fonction en ajoutant un ~ :

~function add x, y
  @result = x + y
var this$ = this;
function add(x, y){
  return this$.result = x + y;
}

Vous pouvez aussi ajouter un ! pour supprimer le return implicite :

util! #=> nothing
!function util(x)
  x
util();
function util(x){
  x;
}

Vous pouvez bien sûr combiner ~ et ! pour créer une fonction liée sans retour implicite.

Fonctions liées

Les fonctions liées sont définies avec une flèche ondulée ~>. Utilisez la version longue pour les fonctions liées et currifiées ~~>.

Les fonctions liées ont leur this lié lexicalement, pas dynamiquement, ce qui signifie que leur this restera l'original, quel que soit le contexte.

obj = new
  @x      = 10
  @normal = -> @x
  @bound  = ~> @x

obj2 = x: 5
obj2.normal = obj.normal
obj2.bound  = obj.bound

obj2.normal! #=> 5
obj2.bound!  #=> 10
var obj, obj2;
obj = new function(){
  var this$ = this;
  this.x = 10;
  this.normal = function(){
    return this.x;
  };
  this.bound = function(){
    return this$.x;
  };
};
obj2 = {
  x: 5
};
obj2.normal = obj.normal;
obj2.bound = obj.bound;
obj2.normal();
obj2.bound();

Référez-vous au paragraphe sur la POO pour plus d'informations sur les fonctions liées dans les classes.

Let, New

let est un raccourci pour (function(a){...}.call(this, b)).

let $ = jQuery
  $.isArray [] #=> true
(function($){
  $.isArray([]);
}.call(this, jQuery));

Vous pouvez aussi définir le this (ou @) avec let.

x = let @ = a: 1, b: 2
  @b ^ 3
x #=> 8
var x;
x = (function(){
  return Math.pow(this.b, 3);
}.call({
  a: 1,
  b: 2
}));
x;

Création de contexte :


dog = new
  @name = \spot
  @mutt = true
#=> {name: 'spot', mutt: true}
var dog;
dog = new function(){
  this.name = 'spot';
  this.mutt = true;
};

Fonctions implicites

Particulièrement utile avec des fonctions de haut niveau telles que map et filter.

(.prop) est un raccourci pour (it) -> it.prop.

map (.length), <[ hello there you ]>
#=> [5,5,3]

filter (.length < 4), <[ hello there you ]>
#=> ['you']
map(function(it){
  return it.length;
}, ['hello', 'there', 'you']);
filter(function(it){
  return it.length < 4;
}, ['hello', 'there', 'you']);

Vous pouvez bien sûr appeler des méthodes :

map (.join \|), [[1 2 3], [7 8 9]]
#=> ['1|2|3','7|8|9']
map(function(it){
  return it.join('|');
}, [[1, 2, 3], [7, 8, 9]]);

(obj.) est un raccourci pour (it) -> obj[it].

obj = one: 1, two: 2, three: 3
map (obj.), <[ one three ]>
#=> [1,3]
var obj;
obj = {
  one: 1,
  two: 2,
  three: 3
};
map(function(it){
  return obj[it];
}, ['one', 'three']);

Rappels désimbriqués

Les rappels désimbriqués (backcalls) sont très utiles. Ils vous permettent de désimbriquer vos rappels. La syntaxe est la même que pour les fonctions, mais avec la flèche vers la gauche (<~, <--, <~~, <-!) :

<- $
alert 'boom'
$(function(){
  return alert('boom');
});

Vous pouvez ajouter des arguments, et réserver un espace si l'ordre des arguments ne vous convient pas :

x <- map _, [1 to 3]
x * 2
#=> [2, 4, 6]
map(function(x){
  return x * 2;
}, [1, 2, 3]);

Si vous voulez rajouter du code après, vous pouvez indenter vos rappels arrières grâce à do.

do
  data <-! $.get 'ajaxtest'
  $ '.result' .html data
  processed <-! $.get 'ajaxprocess', data
  $ '.result' .append processed

alert 'hi'
$.get('ajaxtest', function(data){
  $('.result').html(data);
  $.get('ajaxprocess', data, function(processed){
    $('.result').append(processed);
  });
});
alert('hi');

Si vous voulez des fonctionnalités asynchrones avancées, vous pouvez utiliser async.js avec LiveScript.

Application partielle

Vous pouvez appliquer partiellement des fonctions avec _ comme espace réservé, si l'ordre des arguments ne vous convient pas ou si la fonction n'est pas currifiée :


filter-nums = filter _, [1 to 5]
filter-nums even  #=> [2,4]
filter-nums odd   #=> [1,3,5]
filter-nums (< 3) #=> [1,2]
var filterNums, slice$ = [].slice;
filterNums = partialize$.apply(this, [filter, [void 8, [1, 2, 3, 4, 5]], [0]]);
filterNums(even);
filterNums(odd);
filterNums((function(it){
  return it < 3;
}));
function partialize$(f, args, where){
  var context = this;
  return function(){
    var params = slice$.call(arguments), i,
        len = params.length, wlen = where.length,
        ta = args ? args.concat() : [], tw = where ? where.concat() : [];
    for(i = 0; i < len; ++i) { ta[tw[0]] = params[i]; tw.shift(); }
    return len < wlen && len ?
      partialize$.apply(context, [f, ta, tw]) : f.apply(context, ta);
  };
}

Si vous appelez une fonction partiellement appliquée sans arguments, elle sera éxecutée plutôt que de se retourner elle-même, vous permettant d'utiliser des arguments par défaut

Cette syntaxe est particulièrement intéressante avec le piping lorsqu'utilisée avec des bibliothèques n'ayant pas une syntaxe adaptée (comme underscore.js).

[1 2 3]
|> _.map _, (* 2)
|> _.reduce _, (+), 0
#=> 12
_.reduce(_.map([1, 2, 3], (function(it){
  return it * 2;
})), curry$(function(x$, y$){
  return x$ + y$;
}), 0);
function curry$(f, bound){
  var context,
  _curry = function(args) {
    return f.length > 1 ? function(){
      var params = args ? args.concat() : [];
      context = bound ? context || this : this;
      return params.push.apply(params, arguments) <
          f.length && arguments.length ?
        _curry.call(context, params) : f.apply(context, params);
    } : f;
  };
  return _curry();
}

Arguments

Si vous n'avez qu'un seul argument, vous pouvez utiliser it pour y accéder sans avoir à le lister :

f = -> it + 2
f 3 #=> 5
var f;
f = function(it){
  return it + 2;
};
f(3);

Vous pouvez accéder à l'objet arguments avec le raccourci &. Le premier argument est &0, le second &1, etc. & seul correspond à arguments.


add-three-numbers = -> &0 + &1 + &2
add-three-numbers 1 2 3 #=> 6
var addThreeNumbers;
addThreeNumbers = function(){
  return arguments[0] + arguments[1] + arguments[2];
};
addThreeNumbers(1, 2, 3);

Notez que la currification ne marche pas dans cette situation, car le nombre d'arguments déclarés de add-three-numbers est 0.

Plus

Référez-vous au paragraphe sur la composition de fnctions, ainsi que sur le piping.

If et Unless

Il y a plusieurs manières de formater un if (en fait une expression).

Le standard:

if 2 + 2 == 4
  'quelque chose'
else
  'quelque chose d\'autre'

if 2 + 2 == 4 then 'quelque chose' else 'quelque chose d\'autre'




if 2 + 2 == 4
then 'quelque chose'
else 'quelque chose d\'autre'
if (2 + 2 === 4) {
  'quelque chose';
} else {
  'quelque chose d\'autre';
}
if (2 + 2 === 4) {
  'quelque chose';
} else {
  'quelque chose d\'autre';
}
if (2 + 2 === 4) {
  'quelque chose';
} else {
  'quelque chose d\'autre';
}

Le else est bien sûr optionnel, et vous pouvez ajouter des else if.

if 2 + 2 == 4
  'quelque chose'

if 2 + 2 == 6
  'quelque chose'
else if 2 + 2  == 5
  'quelque chose d\'autre'
else
  'par défaut'
if (2 + 2 === 4) {
  'quelque chose';
}
if (2 + 2 === 6) {
  'quelque chose';
} else if (2 + 2 === 5) {
  'quelque chose d\'autre';
} else {
  'par défaut';
}

Aussi utilisable en tant qu'expression :

result = if 2 / 2 is 0
         then 'quelque chose'
         else 'quelque chose d\'autre'
var result;
result = 2 / 2 === 0 ? 'quelque chose' : 'quelque chose d\'autre';

Aussi utilisable après une expression — ayant une précédence plus basse que l'égalité, rendant ceci possible :

x = 10
x = 3 if 2 + 2 == 4
x #=> 3
var x;
x = 10;
if (2 + 2 === 4) {
  x = 3;
}
x;

unless est l'équivalent de if not.


unless 2 + 2 == 5
  'quelque chose'

x = 10
x = 3 unless 2 + 2 == 5
var x;
if (2 + 2 !== 5) {
  'quelque chose';
}
x = 10;
if (2 + 2 !== 5) {
  x = 3;
}

that réfère implicitement à la valeur de la condition — ignorant les vérifications d'existences (?). Dans un if, dans un switch case ou avec when (et leur version inverse).

time = days: 365



half-year = that / 2 if time.days
#=> 182.5

if /^e(.*)/ == 'enter'
  that.1   #=> 'nter'

if half-year?
  that * 2 #=> 365
var time, that, halfYear;
time = {
  days: 365
};
if (that = time.days) {
  halfYear = that / 2;
}
if (that = /^e(.*)/.exec('enter')) {
  that[1];
}
if ((that = halfYear) != null) {
  that * 2;
}

Boucles et Compréhensions

Les boucles for ont la structure suivante : for (let) (from) (to|til) (by) (when) — (tout est optionnel).

by est l'étape, par défaut 1.

from, si omis, est 0.

when, (alias case ou |) est un guard optionnel (comme un if).

let permet de capturer les variables dans une fonction auto-invoquée, empêchant aussi les variables créées dans la boucle d'exister dans les portées supérieures.

Utilisez in pour itérer dans une liste, of et of dans un objet.

Si utilisé comme expression, les boucles renvoient des listes.


for i from 1 to 10 by 3
  i


for val, i in [7 8 9]
  val




for key, val of {one: 1, two: 2}
  key
var i$, i, ref$, len$, val, key;
for (i$ = 1; i$ <= 10; i$ += 3) {
  i = i$;
  i;
}
for (i$ = 0, len$ = (ref$ = [7, 8, 9]).length; i$ < len$; ++i$) {
  i = i$;
  val = ref$[i$];
  val;
}
for (key in ref$ = {
  one: 1,
  two: 2
}) {
  val = ref$[key];
  key;
}

Les boucles normales (for/while) imbriquées renvoient des listes de listes :

result = for x to 3
  for y to 2
    x + y
result #=> [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5]]
var result, res$, i$, x, lresult$, j$, y;
res$ = [];
for (i$ = 0; i$ <= 3; ++i$) {
  x = i$;
  lresult$ = [];
  for (j$ = 0; j$ <= 2; ++j$) {
    y = j$;
    lresult$.push(x + y);
  }
  res$.push(lresult$);
}
result = res$;
result;

Vous pouvez omettre une ou les deux variable dans une boucle in/of :

res = for , i in [1 2 3]
  i
res #=> [0, 1, 2]

for til 3 then func!
# calls func three times

[6 for til 3] #=> [6, 6, 6]
var res, res$, i$, len$, i;
res$ = [];
for (i$ = 0, len$ = [1, 2, 3].length; i$ < len$; ++i$) {
  i = i$;
  res$.push(i);
}
res = res$;
res;
for (i$ = 0; i$ < 3; ++i$) {
  func();
}
for (i$ = 0; i$ < 3; ++i$) {
  6;
}

Les compréhensions de listes, même multiples, renvoient des listes non imbriquées :

[x + 1 for x to 10 by 2 when x isnt 4]
#=> [1,3,7,9,11]







["#x#y" for x in [\a \b] for y in [1 2]]
#=> ['a1','a2','b1','b2']
var i$, x, ref$, len$, j$, ref1$, len1$, y;
for (i$ = 0; i$ <= 10; i$ += 2) {
  x = i$;
  if (x !== 4) {
    x + 1;
  }
}
for (i$ = 0, len$ = (ref$ = ['a', 'b']).length; i$ < len$; ++i$) {
  x = ref$[i$];
  for (j$ = 0, len1$ = (ref1$ = [1, 2]).length; j$ < len1$; ++j$) {
    y = ref1$[j$];
    x + "" + y;
  }
}

Vous pouvez formater vos compréhensions avec de l'identation :

[{id:id1, name, age} for {id:id1, name} in table1
                     for {id:id2, age} in table2
                     when id1 is id2]
var i$, ref$, len$, ref1$, id1, name, j$, len1$, ref2$, id2, age;
for (i$ = 0, len$ = (ref$ = table1).length; i$ < len$; ++i$) {
  ref1$ = ref$[i$], id1 = ref1$.id, name = ref1$.name;
  for (j$ = 0, len1$ = (ref1$ = table2).length; j$ < len1$; ++j$) {
    ref2$ = ref1$[j$], id2 = ref2$.id, age = ref2$.age;
    if (id1 === id2) {
      ({
        id: id1,
        name: name,
        age: age
      });
    }
  }
}

Les compréhensions d'objets renvoient des objets :

{[key, val * 2] for key, val of {a: 1, b: 2}}
#=> {a: 2, b: 4}
var key, ref$, val;
for (key in ref$ = {
  a: 1,
  b: 2
}) {
  val = ref$[key];
  [key, val * 2];
}

Boucles while :


i = 0
list = [1 to 10]
while n < 9
  n = list[++i]
var i, list, n;
i = 0;
list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
while (n < 9) {
  n = list[++i];
}

until est l'équivalent de while not.

while/until acceptent aussi une clause when, guard optionnel, else ainsi qu'un block else si la boucle n'a pas été évalué.


i = 1
list = [1 to 10]
until i > 7 when n isnt 99
  n = list[++i]
else
  10
var i, list, yet$, n;
i = 1;
list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
for (yet$ = true; !(i > 7);) {
  yet$ = false;
  if (n !== 99) {
    n = list[++i];
  }
} if (yet$) {
  10;
}

do while :


i = 0
list = [1 to 10]
do
  i++
while list[i] < 9
var i, list;
i = 0;
list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
do {
  i++;
} while (list[i] < 9);

while accepte aussi une clause exécutée à chaque itération.


i = 0
list = [1 to 10]
while list[i] < 9, i++ then i
var i, list;
i = 0;
list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
for (; list[i] < 9; i++) {
  i;
}

while true :


i = 0
loop
  \ha
  break if ++i > 20



i = 0
for ever
  \ha
  if ++i > 20
     break
var i;
i = 0;
for (;;) {
  'ha';
  if (++i > 20) {
    break;
  }
}
i = 0;
for (;;) {
  'ha';
  if (++i > 20) {
    break;
  }
}

Switch

break est automatiquement inséré, et les conditions multiples sont autorisées :

switch 6
case 1    then \hello
case 2, 4 then \boom
case 6
  'here it is'
default \something
switch (6) {
case 1:
  'hello';
  break;
case 2:
case 4:
  'boom';
  break;
case 6:
  'here it is';
  break;
default:
  'something';
}

Si vous ne donnez pas d'opérance à switch, ce sera true. (le code compile à switch (false) pour permettre une conversion booléenne via un unique ! plutôt que !!)

switch
case 5 == 6
  \never
case false
  'also never'
case 6 / 2 is 3
  'here'
switch (false) {
case 5 !== 6:
  'never';
  break;
case !false:
  'also never';
  break;
case 6 / 2 !== 3:
  'here';
}

Vous pouvez utiliser fallthrough pour empêcher l'insertion d'un break implicite, et doit être la dernière expression du case. Aussi, vous pouvez utiliser un switch en tant qu'expression :

result = switch 6
case 6
  something = 5
  fallthrough
case 4
  'this is it'

result #=> 'this is it'
var result, something;
result = (function(){
  switch (6) {
  case 6:
    something = 5;
    // fallthrough
  case 4:
    return 'this is it';
  }
}());
result;

| est un alias pour case, et => est un alias pour then. | otherwise et | _ sont des alias pour default :

switch 'moto'
| "quelque chose"  => \coucou
| \explosion \bomb => \boom
| <[ la moto ? ]> => 'ici !'
| otherwise        => \quelquechose
switch ('moto') {
case "quelque chose":
  'coucou';
  break;
case 'explosion':
case 'bomb':
  'boom';
  break;
case 'la':
case 'moto':
case '?':
  'ici !';
  break;
default:
  'quelquechose';
}

Un switch implicite est ajouté après les flèches (comme ->), :, et = si un case (ou |) est trouvé :

func = (param) ->
  | param.length < 5 => param.length
  | otherwise        => param.slice 3

func 'coucou' #=> cou





state = | 2 + 2 is 5 => "I love Big Brother"
        | _          => "I love Julia"
var func, state;
func = function(param){
  switch (false) {
  case !(param.length < 5):
    return param.length;
  default:
    return param.slice(3);
  }
};
func('coucou');
state = (function(){
  switch (false) {
  case 2 + 2 !== 5:
    return "I love Big Brother";
  default:
    return "I love Julia";
  }
}());

Vous pouvez aussi utiliser les switch de CoffeeScript.

day = \Sum
switch day
  when "Lun" then 'Au boulot'
  when "Mar" then 'Au ciné'
  when "Mer" then 'Au café'
  when "Ven", "Sam"
      'Au bingo'
  when "Dim" then 'Toujours au café'
  else 'Au boulot'
var day;
day = 'Sum';
switch (day) {
case "Lun":
  'Au boulot';
  break;
case "Mar":
  'Au ciné';
  break;
case "Mer":
  'Au café';
  break;
case "Ven":
case "Sam":
  'Au bingo';
  break;
case "Dim":
  'Toujours au café';
  break;
default:
  'Au boulot';
}

Attribution

L'assignement basique est comme vous vous y attendez, variable = value, et il n'y a pas besoin de déclarer les variables. Cependant, contrairement à CoffeeScript, vous devez utiliser := pour modifier les variables dans des portées supérieures.


x = 10


do ->
  x = 5

x #=> 10

do ->
  x := 2

x #=> 2
var x;
x = 10;

(function(){
  var x;
  return x = 5;
})();
x;

(function(){
  return x = 2;
})();
x;

Presque tout est une expression, ce qui signifie que vous pouvez faire des choses comme :

x = if 2 + 2 == 4
    then 10
    else 0
x #=> 10
var x;
x = 2 + 2 === 4 ? 10 : 0;
x;

Même les boucles, switch, voire les try/catch sont des expressions.

Si vous voulez simplement déclarer une variable sans l'initialiser, utilisez var.

var x
var x;

Vous pouvez aussi déclarer des constantes avec le mot-clef const. Les vérifications sont faites à la compilation — le JavaScript compilé est le même.

Essayer de compiler le code suivant :

const x = 10
x = 0

Donnera l'erreur suivante : redeclaration of constant "x" on line 2 (re-déclaration de la constante "x" ligne 2).

À l'inverse, les objets ne sont pas "gêlés" — vous pouvez quand même modifier leurs propriétés. Vous pouvez forcer toutes les variables à être des constantes à l'aide de l'option -k (ou --const).

Results in redeclaration of constant "x" on line 2.

Opérateurs

Assignation composée :

(?, ||, or && peuvent préfixer n'importe quelle assignation.)


x = 2    #=> 2
x += 2   #=> 4
x -= 1   #=> 3
x *= 3   #=> 9
x /= 3   #=> 3
x %= 3   #=> 0
x %%= 3  #=> 0
x <?= -1 #=> -1
x >?= 2  #=> 2
x **= 2  #=> 4
x ^= 2   #=> 16

x ?= 10
x        #=> 16

x ||= 5  #=> 16
x &&= 5  #=> 5

x &&+= 3 #=> 8
x ?*= 2  #=> 16

xs = [1 2]
xs ++= [3]
xs #=> [1 2 3]
var x, ref$, xs;
x = 2;
x += 2;
x -= 1;
x *= 3;
x /= 3;
x %= 3;
x = ((x) % (ref$ = 3) + ref$) % ref$;
x <= (ref$ = -1) || (x = ref$);
x >= 2 || (x = 2);
x = Math.pow(x, 2);
x = Math.pow(x, 2);

x == null && (x = 10);
x;

x || (x = 5);
x && (x = 5);

x && (x += 3);
x != null && (x *= 2);

xs = [1, 2];
xs = xs.concat([3]);
xs;

Assignation unaire :


y = \45
+  = y   #=> 45   (conversion en nombre)
!! = y   #=> true (conversion en booléen)
-~-~ = y #=> 3    (conversion en entier doublement incrémentale)
var y;
y = '45';
y = +y;
y = !!y;
y = -~-~y;

Valeurs par défaut — vous pouvez utiliser ||, && et ?.

Vous pouvez utiliser = à la place de ? dans les paramètres et les destructurations :

x ? y = 10
y        #=> 10

f = (z = 7) -> z
f 9      #=> 9
f!       #=> 7
var y, f;
(typeof x == 'undefined' || x === null) && (y = 10);
y;
f = function(z){
  z == null && (z = 7);
  return z;
};
f(9);
f();

Assignation conditionnelle — n'assigne que si l'opérande de droite existe :


age = 21
x? = age
x #=> 21


x? = years
x #=> 21
var age, x;
age = 21;
if (age != null) {
  x = age;
}
x;
if (typeof years != 'undefined' && years !== null) {
  x = years;
}
x;

Destructuration

La déstructuration est une manière puissante d'extraire des valeurs depuis des listes et des objets, vous permettant de lire directement des structures :

[first, second] = [1, 2]
first  #=> 1
second #=> 2
var ref$, first, second;
ref$ = [1, 2], first = ref$[0], second = ref$[1];
first;
second;

Vous pouvez aussi obtenir la partie restante :


[head, ...tail] = [1 to 5]
head #=> 1
tail #=> [2,3,4,5]



[first, ...middle, last] = [1 to 5]
first  #=> 1
middle #=> [2,3,4]
last   #=> 5
var ref$, head, tail, first, i$, middle, last, slice$ = [].slice;
ref$ = [1, 2, 3, 4, 5], head = ref$[0], tail = slice$.call(ref$, 1);
head;
tail;
ref$ = [1, 2, 3, 4, 5], first = ref$[0], middle = 1 < (i$ = ref$.length - 1) ? slice$.call(ref$, 1, i$) : (i$ = 1, []), last = ref$[i$];
first;
middle;
last;

La déstructuration marche aussi avec les objets :


{name, age} = {weight: 110, name: 'emma', age: 20}
name #=> 'emma'
age  #=> 20
var ref$, name, age;
ref$ = {
  weight: 110,
  name: 'emma',
  age: 20
}, name = ref$.name, age = ref$.age;
name;
age;

Vous pouvez aussi labeller les structures avec :label, ou imbriquer les déstructurations :

[[x, ...xs]:list1, [y, ...ys]:list2] = [[1,2,3],[4,5,6]]
x     #=> 1
xs    #=> [2,3]
list1 #=> [1,2,3]
y     #=> 4
ys    #=> [5,6]
list2 #=> [4,5,6]
var ref$, list1, x, xs, list2, y, ys, slice$ = [].slice;
ref$ = [[1, 2, 3], [4, 5, 6]], list1 = ref$[0], x = list1[0], xs = slice$.call(list1, 1), list2 = ref$[1], y = list2[0], ys = slice$.call(list2, 1);
x;
xs;
list1;
y;
ys;
list2;

Sous-déstructuration

Permet de lire et écrire aisément les propriétés d'objets :

mitch =
  age:    21
  height: 180cm
  pets:    [\dog, \goldfish]


phile = {}
phile{height, pets} = mitch
phile.height #=> 180
phile.pets   #=> ['dog', 'goldfish']
var mitch, phile;
mitch = {
  age: 21,
  height: 180,
  pets: ['dog', 'goldfish']
};
phile = {};
phile.height = mitch.height, phile.pets = mitch.pets;
phile.height;
phile.pets;

Accès propriétés

Le standard :

[1 2 3][1]     #=> 2
{a: 1, b: 2}.b #=> 2
[1, 2, 3][1];
({
  a: 1,
  b: 2
}).b;

L'accès avec un point — le point accepte bien plus qu'un simple identifieur comme opérande de droite, vous pouvez aussi donner des nombres, chaînes, parenthèses, etc :

x = "hello world": [4 [5 boom: 6]]
x.'hello world'.1.[0] #=> 5
var x;
x = {
  "hello world": [
    4, [
      5, {
        boom: 6
      }
    ]
  ]
};
x['hello world'][1][0];

Accessignation avec .=.

document.title .= to-upper-case! #=> LIVESCRIPT ...
document.title = document.title.toUpperCase();

Récupérer ou assigner une partie de liste :


list = [1 2 3 4 5]
list[2, 4]    #=> [3,5]
list[1 to 3]  #=> [2,3,4]
list[1 til 3] #=> [2,3]
list[1 til 3] = [7 8]
list          #=> [1,7,8,4,5]
var list, ref$;
list = [1, 2, 3, 4, 5];
[list[2], list[4]];
[list[1], list[2], list[3]];
[list[1], list[2]];
ref$ = [7, 8], list[1] = ref$[0], list[2] = ref$[1];
list;

Une partie d'un objet :

obj = one: 1, two: 2
obj{first: one, two} #=> {first: 1, two: 2}
var obj;
obj = {
  one: 1,
  two: 2
};
({
  first: obj.one,
  two: obj.two
});

L'étoile de longueur *.

list = [1 2 3 4 5]
list[*] = 6
list        #=> [1,2,3,4,5,6]
list[*-1]   #=> 6
var list;
list = [1, 2, 3, 4, 5];
list[list.length] = 6;
list;
list[list.length - 1];

Semi-autovivification .{} (objet), .[] (liste) permet de s'assurer que la propriété est un objet ou une liste :

x = "hello world": [4 [5 boom: 6]]
x.[]'hello world'.1.{}1.boom #=> 6


x.[]arr.{}1.y = 9
x.arr.1.y #=> 9
var x, ref$;
x = {
  "hello world": [
    4, [
      5, {
        boom: 6
      }
    ]
  ]
};
((ref$ = (x['hello world'] || (x['hello world'] = []))[1])[1] || (ref$[1] = {})).boom;
((ref$ = x.arr || (x.arr = []))[1] || (ref$[1] = {})).y = 9;
x.arr[1].y;

L'accès lié .~ récupère une méthode d'un objet en la liant à cet objet. Avec l'auto-insertion de points, vous pouvez simplement utiliser ~.

obj =
  x: 5
  add: (y) -> @x + y

target =
  x: 600
  not-bound: obj.add
  bound: obj~add

target.not-bound 5 #=> 605
target.bound 5     #=> 10
var obj, target;
obj = {
  x: 5,
  add: function(y){
    return this.x + y;
  }
};
target = {
  x: 600,
  notBound: obj.add,
  bound: bind$(obj, 'add')
};
target.notBound(5);
target.bound(5);
function bind$(obj, key, target){
  return function(){ return (target || obj)[key].apply(obj, arguments) };
}

Cascades

Une cascade retourne l'objet accédé, et non pas les opérations

Enchaînement via cascades :


a = [2 7 1 8]
  ..push 3
  ..shift!
  ..sort!
a #=> [1,3,7,8]

document.query-selector \h1
  ..style
    ..color = \red
    ..font-size = \large
  ..inner-HTML = 'LIVESCRIPT!'
var x$, a, y$, z$;
x$ = a = [2, 7, 1, 8];
x$.push(3);
x$.shift();
x$.sort();
a;
y$ = document.querySelector('h1');
z$ = y$.style;
z$.color = 'red';
z$.fontSize = 'large';
y$.innerHTML = 'LIVESCRIPT!';

Les cascades sont appellables, et peuvent contenir n'importe quel code :

console.log
  x = 1
  y = 2
  .. x, y
# affiche `1 2` dans la console
var x$, x, y;
x$ = console.log;
x = 1;
y = 2;
x$(x, y);

Vous pouvez utiliser with pour effectuer la cascade avant l'assignation :

x = with {a: 1, b: 2}
  ..a = 7
  ..b += 9
x #=>  {a: 7, b: 11}
var x, x$;
x = (x$ = {
  a: 1,
  b: 2
}, x$.a = 7, x$.b += 9, x$);
x;

Exceptions

Vous pouvez lancer des exceptions avec throw.

throw new Error 'Une erreur est survenue !'
throw new Error('Une erreur est survenue !');

Vous pouvez attraper les exceptions avec try, catch, finally. Les blocs catch et finally sont optionnels.

Le bloc try est éxecuté. Si une exception est lancée, le bloc catch est éxecuté, avec l'objet. Contrairement au JavaScript, l'exception est liée à la portée de la fonction la plus proche, non pas au bloc catch. Vous pouvez même déstructurer l'exception si vous le voulez :

try
  ...

try
  ...
catch
  2 + 2
e.message

x = try
  ...
catch {message}
  message

x #=> unimplemented
var e, x, message;
try {
  throw Error('unimplemented');
} catch (e$) {}
try {
  throw Error('unimplemented');
} catch (e$) {
  e = e$;
  2 + 2;
}
e.message;
x = (function(){
  try {
    throw Error('unimplemented');
  } catch (e$) {
    message = e$.message;
    return message;
  }
}());
x;

Le bloc finally est éxecuté après le bloc try ou le bloc catch, peu importe qu'il y ait eu une exception ou non.

try
  ...
catch
  handle-exception e
finally
  do-something!

try
  ...
finally
  do-something!
var e;
try {
  throw Error('unimplemented');
} catch (e$) {
  e = e$;
  handleException(e);
} finally {
  doSomething();
}
try {
  throw Error('unimplemented');
} finally {
  doSomething();
}

Programmation orientée objets

Les classes sont simplement une syntaxe offerte pour définir un constructeur et les fonctions du prototype.

Le constructeur est une fonction déclarée directement dans la classe.

Les propriétés du prototypes sont définies avec la même syntaxe que les objets.

class A
  (num) ->
    @x = num
  property: 1
  method: (y) ->
    @x + @property + y

a = new A 3
a.x        #-> 3
a.property #=> 1
a.method 6 #=> 10
var A, a;
A = (function(){
  A.displayName = 'A';
  var prototype = A.prototype, constructor = A;
  function A(num){
    this.x = num;
  }
  A.prototype.property = 1;
  A.prototype.method = function(y){
    return this.x + this.property + y;
  };
  return A;
}());
a = new A(3);
a.x;
a.property;
a.method(6);

Les propriétés statiques (attachées au constructeur) sont définies en préfixant le nom de la propriété par un this (ou @). Ces propriétés peuvent être accédées via le constructor (ou @@) :

class A
  @static-prop = 10
  get-static: ->
    @@static-prop + 2

A.static-prop #=> 10
a = new A
a.get-static! #=> 12
var A, a;
A = (function(){
  A.displayName = 'A';
  var prototype = A.prototype, constructor = A;
  A.staticProp = 10;
  A.prototype.getStatic = function(){
    return constructor.staticProp + 2;
  };
  function A(){}
  return A;
}());
A.staticProp;
a = new A;
a.getStatic();

Les propriétés privées statiques sont juste définies comme des variables dans le bloc de la classe. (note : les propriétés privées pour une instance sont impossibles en JavaScript, et par conséquent, en LiveScript.)

class A
  secret = 10

  get-secret: ->
    secret

a = new A
a.get-secret! #=> 10
var A, a;
A = (function(){
  A.displayName = 'A';
  var secret, prototype = A.prototype, constructor = A;
  secret = 10;
  A.prototype.getSecret = function(){
    return secret;
  };
  function A(){}
  return A;
}());
a = new A;
a.getSecret();

Vous pouvez définir des méthodes liées (using ~>, qui auront alors leur this lié à l'instance :

class A
  x: 10
  bound-func: (x) ~>
    @x
  reg-func: (x) ->
    @x

a = new A
obj =
  x: 1
  bound: a.bound-func
  reg: a.reg-func

obj.bound! #=> 10
obj.reg!   #=> 1
var A, a, obj;
A = (function(){
  A.displayName = 'A';
  var prototype = A.prototype, constructor = A;
  A.prototype.x = 10;
  A.prototype.boundFunc = function(x){
    return this.x;
  };
  A.prototype.regFunc = function(x){
    return this.x;
  };
  function A(){
    this.boundFunc = bind$(this, 'boundFunc', prototype);
  }
  return A;
}());
a = new A;
obj = {
  x: 1,
  bound: a.boundFunc,
  reg: a.regFunc
};
obj.bound();
obj.reg();
function bind$(obj, key, target){
  return function(){ return (target || obj)[key].apply(obj, arguments) };
}

Vous pouvez facilement modifier les propriétés dans les fonctions et dans le constructeur via le raccourci depuis les paramètres :

class A
  (@x) ->

  f: (@y) ->
    @x + @y

a = new A 2
a.x   #=> 2
a.f 3 #=> 5
a.y   #=> 3
var A, a;
A = (function(){
  A.displayName = 'A';
  var prototype = A.prototype, constructor = A;
  function A(x){
    this.x = x;
  }
  A.prototype.f = function(y){
    this.y = y;
    return this.x + this.y;
  };
  return A;
}());
a = new A(2);
a.x;
a.f(3);
a.y;

Si vous définissez le constructeur en tant que fonction liée (~>), vous n'aurez pas besoin d'utiliser new pour l'instanciation :

class A
  (@x) ~>

a = A 4
a.x #=> 4
var A, a;
A = (function(){
  A.displayName = 'A';
  var prototype = A.prototype, constructor = A;
  function A(x){
    var this$ = this instanceof ctor$ ? this : new ctor$;
    this$.x = x;
    return this$;
  } function ctor$(){} ctor$.prototype = prototype;
  return A;
}());
a = A(4);
a.x;

Pour les bibliothèques haut niveau et autres, vous pouvez assigner le constructeur à une fonction externe, en assignant constructor$$. Ce n'est pas recommandé cependant.

f = (@x) ->

class A
  constructor$$: f

a = new A 5
a.x #=> 5
var f, A, a;
f = function(x){
  this.x = x;
};
A = (function(){
  A.displayName = 'A';
  var constructor$$, prototype = A.prototype, constructor = A;
  function A(){
    return constructor$$.apply(this, arguments);
  }
  constructor$$ = f;
  return A;
}());
a = new A(5);
a.x;

Vous pouvez hériter avec extends :

class A
  ->
    @x = 1
  @static-prop = 8
  method: ->
    @x + 2

class B extends A
  ->
    @x = 10

B.static-prop #=> 8
b = new B
b.x       #=> 10
b.method! #=> 12
var A, B, b;
A = (function(){
  A.displayName = 'A';
  var prototype = A.prototype, constructor = A;
  function A(){
    this.x = 1;
  }
  A.staticProp = 8;
  A.prototype.method = function(){
    return this.x + 2;
  };
  return A;
}());
B = (function(superclass){
  var prototype = extend$((import$(B, superclass).displayName = 'B', B), superclass).prototype, constructor = B;
  function B(){
    this.x = 10;
  }
  return B;
}(A));
B.staticProp;
b = new B;
b.x;
b.method();
function extend$(sub, sup){
  function fun(){} fun.prototype = (sub.superclass = sup).prototype;
  (sub.prototype = new fun).constructor = sub;
  if (typeof sup.extended == 'function') sup.extended(sub);
  return sub;
}
function import$(obj, src){
  var own = {}.hasOwnProperty;
  for (var key in src) if (own.call(src, key)) obj[key] = src[key];
  return obj;
}

Vous pouvez utiliser avec super. Si seul, super est une référence à la fonction appropriée. Si vous voulez l'appeler en passant les arguments, utilisez super ....

class A
  ->
    @x = 1
  method: (num) ->
    @x + num

class B extends A
  ->
    @y = 2
    super!

  method: (num) ->
    @y + super ...

b = new B
b.y #=> 2
b.method 10 #=> 13
var A, B, b;
A = (function(){
  A.displayName = 'A';
  var prototype = A.prototype, constructor = A;
  function A(){
    this.x = 1;
  }
  A.prototype.method = function(num){
    return this.x + num;
  };
  return A;
}());
B = (function(superclass){
  var prototype = extend$((import$(B, superclass).displayName = 'B', B), superclass).prototype, constructor = B;
  function B(){
    this.y = 2;
    B.superclass.call(this);
  }
  B.prototype.method = function(num){
    return this.y + superclass.prototype.method.apply(this, arguments);
  };
  return B;
}(A));
b = new B;
b.y;
b.method(10);
function extend$(sub, sup){
  function fun(){} fun.prototype = (sub.superclass = sup).prototype;
  (sub.prototype = new fun).constructor = sub;
  if (typeof sup.extended == 'function') sup.extended(sub);
  return sub;
}
function import$(obj, src){
  var own = {}.hasOwnProperty;
  for (var key in src) if (own.call(src, key)) obj[key] = src[key];
  return obj;
}

Vous pouvez utiliser des mixins via implements. Vous ne pouvez hériter que d'une classe, mais vous pouvez avoir autant de mixins que vous le voulez.

Souvenez-vous, si vous voulez implémenter une classe et non un simple objet, vous devrez implémenter son prototype.

Renameable =
  set-name: (@name) ->
  get-name: -> @name ? @id

class A implements Renameable
  ->
    @id = Math.random! * 1000

a = new A
a.get-name! #=> some random number
a.set-name 'moo'
a.get-name! #=> 'moo'
var Renameable, A, a;
Renameable = {
  setName: function(name){
    this.name = name;
  },
  getName: function(){
    var ref$;
    return (ref$ = this.name) != null
      ? ref$
      : this.id;
  }
};
A = (function(){
  A.displayName = 'A';
  var prototype = A.prototype, constructor = A;
  importAll$(prototype, arguments[0]);
  function A(){
    this.id = Math.random() * 1000;
  }
  return A;
}(Renameable));
a = new A;
a.getName();
a.setName('moo');
a.getName();
function importAll$(obj, src){
  for (var key in src) obj[key] = src[key];
  return obj;
}

Pour modifier le prototype, vous pouvez utiliser le raccourci ::, et vous pouvez utiliser ::= pour modifier plusieurs propriétés :

class A
  prop: 10
  f: ->
    @prop

a = new A
b = new A
a.f! #=> 10

A::prop = 6
a.f! #=> 6
b.f! #=> 6

A ::=
  prop: 5
  f: ->
    @prop + 4
a.f! #=> 9
b.f! #=> 9
var A, a, b, ref$;
A = (function(){
  A.displayName = 'A';
  var prototype = A.prototype, constructor = A;
  A.prototype.prop = 10;
  A.prototype.f = function(){
    return this.prop;
  };
  function A(){}
  return A;
}());
a = new A;
b = new A;
a.f();
A.prototype.prop = 6;
a.f();
b.f();
ref$ = A.prototype;
ref$.prop = 5;
ref$.f = function(){
  return this.prop + 4;
};
a.f();
b.f();

Si vous ne souhaitez pas supporter les anciens navigateurs et voulez utiliser Object.defineProperty, la syntaxe suivante est à votre disposition :

class Box
  dimensions:~
    -> @d
    ([width, height]) -> @d = "#{width}x#height"

b = new Box
b.dimensions = [10 5]
b.dimensions #=> '10x5'
var Box, b;
Box = (function(){
  Box.displayName = 'Box';
  var prototype = Box.prototype, constructor = Box;
  Object.defineProperty(Box.prototype, 'dimensions', {
    get: function(){
      return this.d;
    },
    set: function(arg$){
      var width, height;
      width = arg$[0], height = arg$[1];
      this.d = width + "x" + height;
    },
    configurable: true,
    enumerable: true
  });
  function Box(){}
  return Box;
}());
b = new Box;
b.dimensions = [10, 5];
b.dimensions;

Conversion depuis CoffeeScript

  • Changez toutes vos flèches doublées => pour des flèches ondulées ~>
  • Changez tous vos blocs de commentaires ### ### pour /* */
  • Changez toutes vos séries [x..y] pour [x to y] et changez [x...y] en [x til y]. Si votre série descend, et que ce n'est pas évident (le from et le to ne sont pas littéraux), vous devez ajouter by -1, par exemple [x to -3 by -1]
  • De manière similaire, changez vos for utilisant la syntaxe for i in [x..y] pour for i from x to y, de même pour for i in [x...y] à transformer en for i from x til y
  • Changez vos compréhensions de listes (x for x in list) pour [x for x in list]. Toutes les boucles suffixes qui ne doivent pas retourner des listes ne doivent pas être en suffixes, par exemple, changez increase x for x in list pour for x in list then increase x. Vous pouvez économiser des caractères en utilisant l'alias de then, la flèche doublée =>
  • Changez les nombre littéraux commençant avec un point, transformez .5 en ajoutant un zéro 0.5
  • Changez vos expressions régulières avancées de /// /// vers // //
  • L'opérateur de reste (...) est en préfixe, non pas en suffixe ((...args) ->)
  • Retirez les parenthèses pour les arguments d'une fonction sans paramètres, () -> doit être ->() est toujours un appel.
  • Changez vos constructeurs de :
    class Item
      constructor: ->
    
    Pour :
    class Item
      ->
    
    Si votre constructeur est une fonction externe, assignez constructor$$.
  • Changez vos appels à super de super à) super ...super est une référence directe à la fonction parente et non pas un appel.
  • Entourez vos opérateurs de bits de points, & est .&. et >> est .>>. (etc)
  • Si vous voulez modifier des variables dans des portées supérieures
    x = 10
    do ->
      x = 5
    
    vous douvez utiliser l'opérateur := au lieu du simple = car ce dernier est toujours utilisé pour déclarer une variable (shadowing). Le code ci-dessus devrait être
    x = 10
    do ->
      x := 5
    
  • N'utilisez pas it, that, fallthrough, ou otherwise pour vos variables. Cela peut ne pas être techniquement requis dans tous les cas, mais sera moins confus. Référez-vous à la documentation ci-dessus pour leurs utilisations.
  • LiveScript ignore l'indentation dans les chaînes multilignes avec entourées de guillements simples (non triplés, "string" ou 'string')
    text = "hi 
            there"
    
    devra être changé car produit, en CoffeeScript, "hi         there" mais "hi there" en LiveScript.
  • and, or et les versions espacées de . and ?. ferment les appels implicites : f a .g b or h c est f(a.g(b || h(c))) in CoffeeScript and f(a).g(b) || h(c) in LiveScript. You can use || instead of or and && instead of and as those versions do not close implicit calls.
  • Changez vos dos-bloc pour utiliser let. Par exemple : en CoffeScript do ($ = jQuery) -> $ à changer pour
    let $ = jQuery
      $
    
  • Utilisez le do pour les appels implicites suivis d'un bloc :
    f
      a: b
    
    à transformer en
    f do
      a: b
    
  • Changez vos interpolations JavaScript de `js code here` pour ``js code here``
  • N'espacez jamais vos accès des deux côtés (exemple x . y), c'est l'opérateur de composition de fonctions en LiveScript
  • Espacez vos opérations, comme a-b pour a - b. a-1 et 1-b sont toujours valides (mais déconseillés), car a-b est un identifieur valide en LiveScript, qui se compile en camelCase : aB.

Inspiration

  • Les langages fonctionnels en général
  • Haskell
  • F#

Nom

LiveScript était l'un des premiers noms du JavaScript, c'est une référence.

Remerciements

Vous pouvez trouver la liste des contributeurs ici. Cette liste inclue aussi les contributeurs aux précédesseurs.

Les personnes qui ont contribuées directement, incluant George Zahariev, Satoshi Murakami, Joshua Weinstein, Josh Perez, et Paul Miller.

Un remerciement spécial pour Satoshi car ce projet est un dérivé de son langage Coco et ne serait possible sans.

Traduction par Vendethiel.

Guide du contributeur

Créez un fork de LiveScript et effectuez vos modifications, sans oublier les tests (dossier /test).

Éxecutez bin/slake pour voir les commandes disponibles. Assurez-vous de pouvoir lancer bin/slake build:full — à savoir, recompiler le compilateur. Utile : git checkout -- lib && bin/slake build:full : nettoie votre lib et compile ainsi que lance vos tests. N'envoyez une Pull Request que si tous vos tests passant. Si vous modifiez la grammaire, vous devrez lancer git checkout -- lib && bin/slake build && bin/slake build:parser && bin/slake test. Une fois que tous les tests sont passés, modifiez les parties du compilateur à modifier, puis éxecutez bin/slake build:full.

Ne recompilez pas (extras/livescript.js) (créé par bin/slake build:browser). Ce fichier n'est regénéré que lors de nouvelles versions.