The following is a guest post by Vendethiel. You can submit a guest post as well, just send a pull request.
LiveScript has a lot of tips and tricks which you may not know about.
We'll see in this blog post how you can take advantages of them!
LiveScript offers some basic operator overloading with arrays and strings:
# when the left one is an array literal ['a' 'b'] * 2 # array repetition # when the right one is a string literal <[ foo bar ]> * ', ' # array joining # or when the left one is a string y = 2 'z' * y # string repetition words = text / ' ' # string division # or even when the right one is either a string or a regexp unspaced = text - /\s+/ [\a to \e] * '' - 'b'
var y, words, unspaced, split$ = ''.split, replace$ = ''.replace; ['a', 'b', 'a', 'b']; ['foo', 'bar'].join(', '); y = 2; repeatString$('z', y); words = split$.call(text, ' '); unspaced = replace$.call(text, /\s+/, ''); ["a", "b", "c", "d", "e"].join('').replace('b', ''); function repeatString$(str, n){ for (var r = ''; n > 0; (n >>= 1) && (str += str)) if (n & 1) r += str; return r; }
You can dynamically flag Heregexes with ?
In this case, the last interpolation will be used:
// my? #normal #{[interpolation]} #flag //?
RegExp('my?' + normal + [interpolation], flag);
new
You can use new
alone to create a new context
paul = new @name = 'paul' @age = 25 # LiveScript has real object comprehensions flip = (obj) -> new for own k, v of obj then @[v] = k
var paul, flip; paul = new function(){ this.name = 'paul'; return this.age = 25; }; flip = function(obj){ return new function(){ var k, ref$, v, own$ = {}.hasOwnProperty, results$ = []; for (k in ref$ = obj) if (own$.call(ref$, k)) { v = ref$[k]; results$.push(this[v] = k); } return results$; }; };
JavaScript doesn't allow dynamic keys, because they're not quoted as in JSON.
With LiveScript, you can use parenthesis to get dynamic properties:
foo = 'key' bar = {(foo): 5, "dyna#foo": 6} #=> {'key': 5, 'dynakey': 6} # in destructuring {(+* .>>. 1): middle} = [1 to 7] # middle = 4
var foo, bar, ref$, middle; foo = 'key'; bar = (ref$ = {}, ref$[foo] = 5, ref$["dyna" + foo] = 6, ref$); middle = [1, 2, 3, 4, 5, 6, 7][+[1, 2, 3, 4, 5, 6, 7].length >> 1];
When you destructure you may want default values. With LiveScript, you can use any logic operator, the existence operator (aliased as =
), and even chain them like in any other situation:
{username ? 'root', password || '', secure && 'https' || 'http'} = config<[ USERNAME PASSWORD SECURE ]>
var ref$, username, ref1$, password, secure; ref$ = [config['USERNAME'], config['PASSWORD'], config['SECURE']], username = (ref1$ = ref$.username) != null ? ref1$ : 'root', password = ref$.password || '', secure = ref$.secure && 'https' || 'http';
In LiveScript, parameters accept much more than simple literals. You can assign properties, but also do more complex things, like semiautovivification:
class Person # defaults @name to [], and assigns the first element set-first-name: (@[]names.0) -> # defaults @name to [], and adds the element (* being the array's length) add-name: (@[]names[*]) -> # defaults @hair to {}, and assigns color set-hair-color: (@{}hair.color) -> p = new Person p.hair # undefined p.set-hair-color "#8b0000" p.hair.color # "#8b0000" p.names # undefined p.add-name 'George' p.add-name 'Jean' p.names # ["George", "Jean"] p.set-first-name "Paul" p.names # ["Paul", "Jean"]
var Person, p; Person = (function(){ Person.displayName = 'Person'; var prototype = Person.prototype, constructor = Person; prototype.setFirstName = function($0){ (this.names || (this.names = []))[0] = $0; }; prototype.addName = function(arg$){ var ref$; (ref$ = this.names || (this.names = []))[ref$.length] = arg$; }; prototype.setHairColor = function(color){ (this.hair || (this.hair = {})).color = color; }; function Person(){} return Person; }()); p = new Person; p.hair; p.setHairColor("#8b0000"); p.hair.color; p.names; p.addName('George'); p.addName('Jean'); p.names; p.setFirstName("Paul"); p.names;
Array and Object can be sliced in pretty much any way. You probably know about a[0 1]
, and we saw a<[foo bar]>
, but you can do much more. For example, you can use splats:
posts['op' ...'replies'] = thread # even with objects extended = {...base, +extended}
var the, end, extended, ref$, slice$ = [].slice; posts['op'] = thread[0], posts['replies'] = slice$.call(thread, 1); extended = (ref$ = {}, import$(ref$, base), ref$.extended = true, ref$); function import$(obj, src){ var own = {}.hasOwnProperty; for (var key in src) if (own.call(src, key)) obj[key] = src[key]; return obj; }
The low-precedence backpipe can be used for function evaluation, not only to chain arguments without parenthesis but much more, it can (for example) allow you to use do:
# basic backpipe usage toCase \up <| \hello # let's say that, if we have no page, we need to create an un element # with a li element containing 1 pages.appendChild <| do node 'ul' className: 'ui-pagination' ..appendChild node 'li' innerHTML: '1'
var x$; toCase('up')('hello'); pages.appendChild((x$ = node('ul', { className: 'ui-pagination' }), x$.appendChild(node('li', { innerHTML: '1' })), x$));
Do can be used to change the order or to give you more space to write some complex expressions
td = node 'td' class: 'informations' innerHTML: do i18n.get-translation 'informations' .format-for @user # use do and backcall promises = value: do <- $.get 'a'
var td, promises; td = node('td', { 'class': 'informations', dataType: 'info', innerHTML: i18n.getTranslation('informations').formatFor(this.user) }); promises = { value: $.get('a', function(){}) };
You often need to defer a call to a function, keeping the same context and arguments. You can use the thisplat for that:
call = -> call-instead ...
var call; call = function(){ return callInstead.apply(this, arguments); };
If you need to use labels, you can use them with a block or an expression:
# labeling a function gives it a name :refresh let wait '10s' !-> console.log 'timeout !' refresh! # label a for :serie for i to 100 for j to 100 if (Math.floor Math.random! * 11) > 8 continue serie console.log "#i:#j"
var i$, i, j$, j; (function refresh(){ wait('10s', function(){ console.log('timeout !'); refresh(); }); }.call(this)); serie: for (i$ = 0; i$ <= 100; ++i$) { i = i$; for (j$ = 0; j$ <= 100; ++j$) { j = j$; if (Math.floor(Math.random() * 11) > 8) { continue serie; } console.log(i + ":" + j); } }
You can use ,
to mean a no-op literal
And splat arrays or arguments
[a, b, c, ,, d] = foo [the, ..., end] = my-friend a = (, b) -> b # b is a's second argument # forces the closure's argument list to be empty test 'it works' (...) -> it # it wasn't shadowed
var a, b, c, d, the, end; a = foo[0], b = foo[1], c = foo[2], d = foo[5]; the = myFriend[0], end = myFriend[myFriend.length - 1]; a = function(arg$, b){ return b; }; test('it works', function(){ return it; });
For more information on LiveScript, check out the LiveScript site.
For more on LiveScript and prelude.ls, follow @gkzahariev.
comments powered by Disqus