×
 

概述

LiveScript 是一种编译成 JavaScript 的语言。它与 JavaScript 具有直接的映射关系,并允许您编写简洁的代码,避免重复的样板代码。虽然 LiveScript 添加了许多功能来辅助函数式编程,但它也对面向对象和命令式编程进行了许多改进。

LiveScript 是 Coco 的分支,并且是 CoffeeScript 的间接后代,它与 CoffeeScript 具有很强的兼容性

npm install -g livescript

以获取 LiveScript 的更新。

特色博文:LiveScript 1.4.0 - 源映射及更多!

双击示例将其加载到编译器/REPL 中。法语文档:Français

一些示例

LiveScript
# Easy listing of implicit objects
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




# Implicit access, accessignment
up-case-name = (.name .= to-upper-case!)

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











# operators as functions, piping
map (.age), table2 |> fold1 (+)
#=> 67 ('fold1' and 'map' from 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();
}

无嵌套回调和无括号链式调用

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

安装

您可以通过 npm 安装 LiveScript:sudo npm install -g livescript

或者,您也可以下载它(ziptar.gz),进入其目录,然后运行 sudo make install。使用 git 下载:git clone git://github.com/gkz/LiveScript.git && cd LiveScript && sudo make installNode.js 需要安装在您的系统上。

您也可以通过在 LiveScript/browser/livescript.js 中包含文件并通过脚本标签将其包含在浏览器中直接使用它。然后,您必须调用 require("livescript").go()。如果您使用此方法,则您的 LiveScript 脚本必须放在包含的 livescript.js 文件之后,并且脚本标签必须具有 type="text/ls" 属性。

例如

<script src="livescript.js"></script>
<script type="text/ls">
    console.log "boom #{window.location}"
</script>
<script>
    var LiveScript = require("livescript");
    LiveScript.go();
</script>

使用

命令行

用法:lsc [options]... [file]...

不带任何选项使用 lsc 启动 REPL。

常规

Usage: lsc [option]... [file]...

Use 'lsc' with no options to start REPL.

Misc:
  -v, --version              display version
  -h, --help                 display this help message
  -c, --compile              compile to JavaScript and save as .js files
  -e, --eval code::String    pass as string from the command line as input
  -d, --prelude              automatically import prelude.ls in REPL
  -r, --require paths::[String]  require libraries before executing
  -s, --stdin                read stdin
  -j, --json                 print/compile as JSON
  -n, --nodejs               pass options after this through to the 'node' binary
  -w, --watch                watch scripts for changes, and repeat
  -k, --const                compile all variables as constants

Output control:
  -o, --output path::String  compile into the specified directory
  -p, --print                print the result to stdout
  -b, --bare                 compile without the top-level function wrapper
  --no-header                do not add "Generated by" header
  -l, --lex                  print the tokens the lexer produces
  -t, --tokens               print the tokens the rewriter produces
  -a, --ast                  print the syntax tree the parser produces
  --debug                    print debug output when compiling
  -m, --map String           generate source maps - either: 'none', 'linked',
                             'linked-src', 'embedded', or 'debug'
  --no-warn                  suppress compiler warnings

Version 1.6.0
<https://livescript.node.org.cn/>

命令行示例

  • 运行 LiveScript 文件(通过 node.js):lsc file.ls - 您可以省略 .ls
  • 编译 LiveScript 文件,创建 file.jslsc -c file.ls
  • 监视文件并在更改时编译:lsc -wc file.ls
  • 编译目录并输出到另一个目录:lsc -co output src
  • 监视目录,并在更改时编译到输出目录:lsc -wco output src
  • 编译单行代码并打印 JS:lsc -bpe '[1 to 5]'
  • 启动 LiveScript REPL:lsc - 使用 Ctrl-D 退出,使用 Ctrl-J 进行多行输入。

JSON

您可以通过使用 .json.ls 文件扩展名或通过传入 -j,--json 标志来编译 LiveScript 样式的 JSON 到 .json 文件。

您可以将 -e,--eval 与 JSON 数据结合使用,以实现函数式命令行 JSON 处理。您提供的 JSON 数据在 eval 表达式中绑定到 this。您可以将 JSON 数据通过管道输入,并使用 -j,--json 标志,或者使用扩展名为 .json 的文件。

$ lsc -e '@name' package.json
"livescript"
$ cat package.json | lsc -je '@name'
"livescript"

您可以使用 -d,--prelude 标志包含 prelude.ls

$ lsc -de '@files |> map (.to-upper-case!)' package.json
[
  "LIB",
  "BIN",
  "README.MD",
  "LICENSE"
]

您也可以轻松地使用 -r,--require 标志引入模块。

lsc -r 'path' -de '@files |> map -> [it, path.resolve it]' package.json
[
  [
    "lib",
    "/home/z/open-source/LiveScript/lib"
  ],
  [
    "bin",
    "/home/z/open-source/LiveScript/bin"
  ],
  [
    "README.md",
    "/home/z/open-source/LiveScript/README.md"
  ],
  [
    "LICENSE",
    "/home/z/open-source/LiveScript/LICENSE"
  ]
]

编程 API

使用 var LiveScript = require('livescript');require! livescript 将其引入您的 Node 或 Browserify 项目,或使用上述步骤进行浏览器安装。在 Node 中,引入此模块也会将其注册到 require.extensions 中。

常规

LiveScript.compile(code :: String, options :: Object?) -> String

将 LiveScript 代码字符串编译成纯 JavaScript。如果字符串编译失败,则会抛出 SyntaxError。

选项

bare :: Boolean = false如果为 true,则不使用顶级函数包装器进行编译
header :: Boolean = true如果为 true,则添加“Generated by”标头
const :: Boolean = false如果为 true,则将所有变量编译为常量
json :: Boolean = false如果为 true,则编译为 JSON 而不是 JavaScript
warn :: Boolean = true如果为 false,则抑制编译器警告
filename :: String?用于编译错误的可选文件名

LiveScript.run(code :: String, options :: Object?) -> String

评估 LiveScript 代码字符串。如果字符串编译失败,则会抛出 SyntaxError。请注意,这使用 Function 构造函数。

选项

const :: Boolean = false如果为 true,则将所有变量编译为常量
filename :: String?用于错误的可选文件名

实用程序

LiveScript.ast(code :: String|Array) -> Object

生成 LiveScript 源代码的 AST 表示形式,如果它是字符串,则生成令牌流,如果它是数组,则生成令牌流。如果它是一个无法解析为 LiveScript 代码的字符串,则会抛出 SyntaxError。如果它是一个数组,如果流无效,则会抛出 Error。


LiveScript.tokens(code :: String, options :: Object?) -> Array

从 LiveScript 代码生成令牌流。请注意,这不会在调用之间保持状态。

选项

raw :: Boolean = false如果为 true,则在标记化之前不刷新令牌流 - 建议保持默认值
line :: Number = 0令牌流的起始行号

LiveScript.lex(code :: String)

等价于 LiveScript.tokens(code, {raw: true})


LiveScript.ast.* :: Object...

用于 LiveScript.ast() 的所有 AST 构造函数。

浏览器特定方法

LiveScript.stab(code :: String, callback :: (err :: Error?) -> void, filename :: String?) -> void

运行代码字符串,并使用可选错误进行回调。


LiveScript.load(url :: String, callback :: (err :: Error?) -> void) -> void

通过 XMLHttpRequest 加载 url 处的远程 LiveScript 文件,并使用可选错误进行回调。


LiveScript.go() -> void

加载所有具有 type 属性为 "text/ls""application/ls" 的脚本。

文本编辑器/IDE 插件

  • Vim 用户,请查看 vim-ls
  • LiveScript 的 TextMate、Chocolat 和 Sublime Text 捆绑包:LiveScript.tmbundle
  • Emacs 的基本 LiveScript 支持:livescript-mode
  • 以及更多关于 wiki 页面。将您的项目添加到不断增长的列表中!

标准库

prelude.ls 是使用 LiveScript 时推荐的基本库。它允许您执行以下操作

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

您可以使用 -d--prelude 选项自动将 prelude.ls 导入到 REPL 中。

Prelude 在此页面上加载,您可以在右侧的编译器/REPL 中运行内容时使用它。


源映射

编译时使用 -m, --map 选项生成源映射。它有几个可能的值,none - 默认值,linkedlinked-srcembeddeddebug

生成源映射时涉及三个文件

  1. 原始 LiveScript 源代码
  2. 源映射
  3. 生成的 JavaScript 源代码

a 可以选择性地嵌入到 b 中,b 可以选择性地嵌入到 c 中,通过注释。

linked:不嵌入,c 通过相对路径链接到 bb 也链接到 a

linked-srcb 嵌入到 c 中,但 a 链接到

embedded:所有内容都嵌入到 c

debug:与 linked 相同,但也会将源节点树的可读表示形式(类似于 ast 选项的输出)输出到 '.map.debug' 文件中。

如果您直接将 lsc 的输出提供给浏览器(即不进行进一步处理),请使用 linkedlinked-src。它们使原始源代码保持分离,因此 JavaScript 文件仍然很小。linked-src 只是意味着您需要携带的文件更少,但代价是增加了 JavaScript 文件的大小。

对于其他所有情况,请使用 embedded - 它自包含,并且是大多数其他工具(如 browserify)作为输入接受的唯一形式。文件将明显变大,但您可以在构建管道的末尾运行一个单独的工具,将输出拆分回 linked 形式。

简介

像许多现代语言一样,块由空格缩进分隔,换行符用于代替分号终止语句(如果要在一行上放置多个语句,您仍然可以使用分号)。

例如(左侧为 LiveScript,右侧为编译后的 JavaScript)

if 2 + 2 == 4
  do-something()
if (2 + 2 === 4) {
  doSomething();
}

您可以使用右侧的 LiveScript 编译器/REPL 自己尝试所有这些示例。

为了进一步澄清,在调用函数时可以省略括号。

add 2, 3
add(2, 3);

并且注释是

# from here to the end of the line.
// from here to the end of the line.

Lisp 黑客们,您可能会高兴地知道,您可以在变量和函数的名称中使用连字符。这些名称等价于驼峰命名法,并编译成驼峰命名法。例如 my-value = 42 == myValue = 42

LiveScript 的文件扩展名为 .ls

定义函数

在 LiveScript 中定义函数非常轻量级

(x, y) -> x + y



-> # an empty function

times = (x, y) ->
  x * y
# multiple lines, and be assigned to
# a var like in JavaScript
var times;
(function(x, y){
  return x + y;
});

(function(){});

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


如您所见,函数定义相当简短!您可能还注意到我们省略了 return。在 LiveScript 中,几乎所有内容都是表达式,并且自动返回最后一个表达式。但是,如果您愿意,您仍然可以使用 return 强制返回,并且可以在箭头之前添加一个感叹号 ! 来抑制自动返回 no-ret = (x) !-> ...

赋值

基本赋值与您预期的一样,variable = value,并且不需要变量声明。但是,与 CoffeeScript 不同,您必须使用 := 来修改上层作用域中的变量。

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;

几乎所有内容都是表达式,这意味着您可以执行以下操作

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

x;

诸如循环、switch 语句,甚至 try/catch 语句都是表达式。

如果只想声明一个变量而不初始化它,可以使用 var

var x
var x;

您也可以使用const在LiveScript中声明常量。它们在编译时进行检查 - 编译后的JS没有区别。

尝试编译以下内容

const x = 10
x = 0

导致在第 2 行重新声明常量“x”

但是,如果将对象声明为常量,则不会冻结它们 - 您仍然可以修改其属性。如果使用-k--const标志进行编译,则可以强制所有变量都为常量。

信息

有关与CoffeeScript的区别,请参阅CoffeeScript到LiveScript转换指南

您可以双击任何示例将LiveScript代码加载到右侧的编译器中,或者您可以随意尝试自己的代码。按运行以执行编译后的JavaScript。请注意,LiveScript将编译后的JS包装在一个安全包装器(function(){...contents...}).call(this);中 - 为简洁起见,所有示例和此页面的编译器输出中都省略了此包装器。

字面量

数字

.4无效,必须以零开头,例如0.4

42
17.34
0.4
42;
17.34;
0.4;

下划线和附加的字母将被忽略。

64_000km
64000;

可以使用~使用2到36之间的任何基数。

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

布尔值、空值、Null

与CoffeeScript中的别名相同。

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

在JavaScript中,可以重新定义undefined,因此谨慎使用始终产生未定义值的void运算符。

顶级void(不用作表达式)编译为空(用作占位符) - 必须将其用作要编译的值。

void
x = void

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

null;

字符串

您可以使用双引号或单引号。

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

字符串可以用反斜杠而不是引号编写。反斜杠字符串不能包含, ; ] ) }或空格。

\word
func \word, \word;
(func \word)
[\word]
{prop:\word}
'word';
func('word', 'word');
func('word');
['word'];
({
  prop: 'word'
});

双引号字符串允许插值。单引号字符串按原样传递。简单的变量可以在没有花括号的情况下进行插值。

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

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

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

在您的插值字符串前面加上%将返回原始部分作为数组。这允许您根据需要连接结果。

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

多行字符串(也可以用双引号执行相同操作,以便与插值一起使用)

multiline = 'string can be multiline \
            and go on and on \
            beginning whitespace is \
            ignored'
heredoc = '''
            string can be multiline
            with newlines
            and go on and on
            beginning whitespace is
            ignored
'''
nospaces = 'deadbeef
            deadbeef'
var multiline, heredoc, nospaces;
multiline = 'string can be multiline and go on and on beginning whitespace is ignored';

heredoc = 'string can be multiline\nwith newlines\nand go on and on\nbeginning whitespace is\nignored';





nospaces = 'deadbeefdeadbeef';

注释

单行注释以#开头。它们不会传递到编译后的输出中。

# single line comment

多行注释保留在输出中。

/* multiline comments
   use this format and are preserved
   in the output unlike single line ones
*/
/* multiline comments
   use this format and are preserved
   in the output unlike single line ones
*/

对象

花括号是可选的

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
};

动态键

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

属性设置简写 - 如果希望属性名称与变量名称相同,则可以轻松地使用变量设置属性。

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

标记简写 - 轻松设置布尔属性。

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

This - 无需使用点.来访问属性。

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

正则表达式

使用单个/分隔的常规正则表达式。

/moo/gi
/moo/gi;

//分隔 - 多行、注释、空格!

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





列表

用括号分隔的常规列表文字

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

如果前面的项目不可调用,则不需要逗号

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

使用缩进块创建隐式列表。它们至少需要两个项目才能工作。如果您只有一个项目,则可以添加一个yaddayaddayadda...来强制使用隐式列表。

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

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



oneItem = [1];



在隐式列出时,可以使用星号*来消除诸如隐式对象和隐式列表之类的隐式结构的歧义。星号不表示列表的项目,而仅仅是设置一个隐式结构,以便它不会与正在列出的其他结构混淆。

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
}];

单词列表

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

范围

to表示直到且包括该数字。til表示直到但不包括该数字。

您可以选择添加一个by,它定义范围的步长。

如果您省略第一个数字,则假定为0

使用数字/字符串文字

[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"];

使用任何表达式 - 如果您的范围使用表达式,并且希望它向下(即从较大的数字到较小的数字),则必须显式设置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$;
}

其他

标签(对嵌套循环很有用)

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

constructor简写。

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

Yaddayaddayadda - 一个占位符

...
throw Error('unimplemented');

运算符

数字

标准数学运算符

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

有一个余数运算符,就像在JavaScript中一样 - 但我们还添加了一个正确的模运算符。

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

幂是右结合的,并且优先级高于一元运算符。^**的别名

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));

增量和减量

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;

按位和移位运算符

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;

转换为数字

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

比较

严格相等(无类型强制)

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;

模糊相等(带类型强制)

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

大于/小于

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

链式比较

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

最小值/最大值 - 返回两个操作数中较小/较大的一个。

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

当其中一个操作数等于(==is,以及它们的否定)是正则表达式文字时,它将针对该操作数测试另一个操作数。相等编译为exec,因此您可以使用结果,而否定则简单地编译为test

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

逻辑

基础知识

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;

其他语言中不常见的逻辑运算符 - 异或

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);

andorxor关闭隐式调用,而||&&则不关闭。

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

您可以调用逻辑运算符。

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

In/Of

使用in检查元素是否在列表中;使用of检查键是否在对象中。

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;
}

管道

您可以将值传递进来,而不是一系列嵌套的函数调用。x |> ff <| x等效于f(x)

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


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

您可以使用换行符来更好地分隔内容。

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

函数

组合允许您通过组合一系列函数来创建函数。LiveScript有两个用于组合的运算符,前向>>和后向<<

(f << g) x等效于f(g(x)),而(f >> g) x等效于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; }

更清楚地说明这两个运算符之间的区别

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;
  };
}

您可以使用空格点作为<<的别名,例如f . g,就像在Haskell中一样。

列表

您可以将两个列表连接在一起

<[ one two three ]> ++ [\four]
#=> ['one','two','three','four']
['one', 'two', 'three'].concat(['four']);


请注意,连接运算符必须在两侧都留有空格xs ++ ys,或者在两侧都不留空格xs++ys。如果只在一侧留有空格,则将其视为增量运算符。

当第一个是列表文字时的列表重复

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

当右侧操作数是字符串文字时的连接

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

一元展开 - 当操作数是列表文字时,将一元运算符应用于每个项目

r = +...[4 5 6]          #=> [+4, +5, +6]
t = typeof! ...[\b 5 {}] #=> ["String", "Number", "Object"]
c = ~...[4, 5]           #=> [-5, -6]
++...player<[strength hp]>
# also works with -, --, typeof, ! and delete!
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;
}

字符串

字符串连接

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

当第一个操作数是字符串文字时的字符串重复

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

当右侧操作数是字符串或正则表达式文字时的字符串减法/除法 - 减法表示replace,除法表示split

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

存在

?运算符可以在各种上下文中用于检查是否存在。

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;
}

对象

Instanceof - 右侧的列表文字将展开

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 - 添加一个感叹号以获得有用的替代方法

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

Delete返回已删除项目的value

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!类似于JavaScript中的delete,并且仅当属性存在且无法删除时才返回false,否则返回true。

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;

属性复制 - 将可枚举属性从右复制到左,并计算为左。<<<用于自己的属性,<<<<用于所有属性。importimport all分别是这两个的别名,区别在于,如果您省略左操作数,则假定为this

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;
}

克隆 - 创建操作数的原型克隆。它不会创建对象的深度克隆,而是将结果对象的原型设置为操作数。请记住,在序列化为JSON时会忽略原型。

obj = {one: 1}


obj2 = ^^obj
obj2.two = 2
obj2 #=> {one: 1, two: 2}
# above includes its prototype's properties
# JSON serialization would be just `{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;
}

中缀with(也称为cloneport)结合了克隆和属性复制运算符,以便于创建对象。它等效于^^obj <<< obj2。请记住,克隆运算符创建原型克隆,并且原型不会在JSON中序列化。

girl = {name: \hanna, age: 22}
guy  = girl with name: \john
guy  #=> {name: 'john',  age: 22}
# the above result include the object's prototype
# in the result - the actual JSON: {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;
}

部分应用、运算符作为函数

您可以部分应用运算符并将其用作函数

(+ 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; }

export

通过使用export运算符而不是exports,您可以获得一种更简洁的定义模块的方法。



export func = ->

export value

export value-a, value-b, value-c



export
  a: 1
  b: -> 123



export class MyClass
var func, ref$, MyClass, out$ = typeof exports != 'undefined' && exports || this;

out$.func = func = function(){};

out$.value = value;

out$.valueA = valueA;
out$.valueB = valueB;
out$.valueC = valueC;

ref$ = out$;
ref$.a = 1;
ref$.b = function(){
  return 123;
};

out$.MyClass = MyClass = (function(){
  MyClass.displayName = 'MyClass';
  var prototype = MyClass.prototype, constructor = MyClass;
  function MyClass(){}
  return MyClass;
}());

require!

需要一系列模块会导致很多冗余代码。您可以使用require!摆脱这些冗余代码,它接受ID或字符串、数组或对象文字。

如果您需要名称中带有连字符的模块,则必须使用字符串文字。

您可以使用对象文字重命名您需要的内容。

您可以解构以获取value的内容。

require! lib
require! 'lib1'

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

require! [fs, path]

require! <[ fs path ]>


require! jQuery: $

require! {
  fs
  path
  lib: foo
}
var lib, lib1, preludeLs, fs, path, $, foo;
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');
foo = require('lib');


您可以轻松地使用解构需要模块的部分内容。

require! {
    fs: filesystem
    'prelude-ls': {map, id}
    path: {join, resolve}:p
}
var filesystem, ref$, map, id, p, join, resolve;
filesystem = require('fs');
ref$ = require('prelude-ls'), map = ref$.map, id = ref$.id;
p = require('path'), join = p.join, resolve = p.resolve;

文件名会自动提取。

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

函数

在 LiveScript 中定义函数非常轻量级

(x, y) -> x + y


-> # an empty function

times = (x, y) ->
  x * y
# multiple lines, and be assigned to
# a var like in JavaScript
var times;
(function(x, y){
  return x + y;
});
(function(){});

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

如您所见,函数定义要短得多!您可能还注意到我们省略了return。在LiveScript中,几乎所有内容都是表达式,并且自动返回达到的最后一个表达式。

您可以用感叹号!前缀函数箭头以抑制自动返回。

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

调用

在调用函数时,您可以省略括号,并且如果前面的项目不可调用,则可以省略分隔参数的逗号,就像在数组中一样。

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

如果您没有参数地调用函数,则可以使用感叹号! - 此外,在链接带感叹号的函数时,您不需要使用点。

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

andorxor、带空格的.?.都会关闭隐式调用 - 允许无括号链接。

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

您可以使用do调用没有参数的函数

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

如果您在命名函数上使用do,当do不用作表达式时,命名函数将保持为函数语句。

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;

您不能用隐式对象调用函数,如果您想这样做,可以使用do

func do
  a: 1
  b: 2
func({
  a: 1,
  b: 2
});

do允许您在不添加额外括号的情况下执行许多操作。

pow do
  1
  2

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


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

您还可以使用反引号`以中缀方式调用函数。

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

使用裸露的splat...调用函数意味着使用当前函数的参数调用它。在调用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);

参数

扩展参数

set-person-params = (
  person # target object to set params
  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);

这在使用this时尤其有用。

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

您可以设置默认参数

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();

...或者确实使用任何逻辑运算符(在参数中,x = 2只是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);

您还可以解构参数

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
});

...甚至在那些解构的参数上设置默认值(或使用任何逻辑),其功能类似于Python的关键字参数。

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();

您也可以在参数中使用splat

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);

您甚至可以在参数中使用一元运算符。您可以使用+!!将参数分别转换为数字或布尔值,或者使用克隆运算符^^来确保您对对象的任何更改都不会反映在原始对象中。您仍然可以使用扩展参数,例如(!!x.x) ->

f = (!!x) -> x
f 'truthy string' #=> true




g = (+x) -> x
g '' #=> 0




obj = {prop: 1}
h = (^^x) ->
  x.prop = 99
  x
h obj
obj.prop #=> 1
var f, g, obj, h;
f = function(x){
  x = !!x;
  return x;
};
f('truthy string');

g = function(x){
  x = +x;
  return x;
};
g('');

obj = {
  prop: 1
};
h = function(x){
  x = clone$(x);
  x.prop = 99;
  return x;
};
h(obj);
obj.prop;

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

柯里化

柯里化函数非常强大。本质上,当调用时提供的参数少于定义的参数时,它们会返回一个部分应用函数。这意味着它返回一个函数,其参数是您未提供的参数,并且已绑定您提供的参数的值。它们在 LiveScript 中使用长箭头定义。也许一个例子会让事情更清楚

times = (x, y) --> x * y
times 2, 3       #=> 6 (normal use works as expected)
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();
}

您可以使用长波浪箭头定义绑定柯里化函数:~~>

如果您不带任何参数调用柯里化函数,它将按原样计算,允许您使用默认参数。

f = (x = 5, y = 10) --> x + y
f! #=> 15
g = f 20
g 7 #=> 27
g!  #=> 30
var f, g;
f = curry$(function(x, y){
  x == null && (x = 5);
  y == null && (y = 10);
  return x + y;
});
f();
g = f(20);
g(7);
g();
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();
}

命名函数

您可以创建命名函数,其定义提升到作用域的顶部 - 这对于在文件末尾而不是顶部定义实用函数很有用。命名函数是常量,不能重新定义。

util!  #=> 'available above declaration'
util2! #=> 2

function util
  'available above declaration'
function util2 then 2
util();
util2();
function util(){
  return 'available above declaration';
}
function util2(){
  return 2;
}

您可以在函数定义前加上 ~ 以将其设为绑定函数。

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

您可以在其前面加上感叹号 ! 以抑制返回值。

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

如果需要,您可以组合 ~! 以创建一个绑定的非返回值函数。

绑定函数

使用波浪箭头 ~> 定义。对于柯里化和绑定函数,使用长波浪箭头 ~~>。在命名函数前加上 ~ 以将其设为绑定函数。

绑定函数的 this 是词法绑定的,而不是像通常那样动态绑定的。这意味着无论在哪个上下文中调用它们,其主体中 this 的值始终是定义它们时 this 的值。

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();

查看 OOP 部分,了解在类中使用绑定函数时的更多信息。

Let,New

let(function(a){...}.call(this, b)) 的简写。

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

您还可以使用 let 定义 this(也称为 @)。

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;

使用新上下文

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

访问/调用函数简写

它们对高阶函数(如 map 和 filter)特别有用。

(.prop)(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']);

您也可以用它来调用方法

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.)(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']);

回调

回调非常有用。它们允许您取消嵌套回调。它们使用指向左边的箭头定义。所有语法与定义绑定函数 (<~)、柯里化函数 (<--, <~~)、抑制返回值 (<-!) 的常规箭头相同 - 只是方向相反。

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

它们可以接受参数,您还可以指定要放置参数的位置。

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

如果希望在回调之后有更多代码,可以使用 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');

异步和等待

如果您将在支持 asyncawait JavaScript 关键字的平台上运行编译后的代码,那么您可以在 LiveScript 中编写真正的异步函数。要将函数标记为异步,请在函数箭头中添加一个额外的 > (->>~>>-->> 等),或者对于命名函数,编写 async function 而不是 function。在异步函数内部,您可以像在 JavaScript 中一样使用 await 关键字。

f1 = (x) ->> await x


async function f2 x
  a = await f1 x

var f1;
f1 = async function(x){
  return (await x);
};
async function f2(x){
  var a;
  return a = (await f1(x));
}

部分应用

您可以使用下划线 _ 作为占位符来部分应用函数。有时,您要处理的函数不是柯里化函数,或者如果它是柯里化函数,则参数的顺序不佳。在这些情况下,部分应用非常有用。

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);
  };
}

如果您不带任何参数调用部分应用函数,它将按原样执行,而不是返回自身,允许您使用默认参数。

如果使用的函数没有很好的参数顺序并且不是柯里化函数(例如 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();
}

参数

如果您只有一个参数,可以使用 it 访问它,而无需定义参数。

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

您可以使用简写 & 访问 arguments 对象。第一个参数是 &0,第二个是 &1,依此类推。单独的 & 是整个 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);

请注意,在这种情况下柯里化不起作用,因为 add-three-numbers 中声明的参数数量为 0。

更多

还可以查看有关 组合函数管道 的部分。

生成器和 Yield

您可以在 LiveScript 代码中使用生成器和 yield!简要概述

function* f
    yield "foo"

g = ->*
    yield from f!
    yield "bar"

h = g!
h.next!.value + h.next!.value #=> "foobar"
var g, h;
function* f(){
  return (yield "foo");
}
g = function*(){
  (yield* f());
  return (yield "bar");
};
h = g();
h.next().value + h.next().value;

您可以通过在 function 关键字后附加星号 * 或将其附加到 LiveScript 的箭头表示法来创建生成器。这适用于我们拥有的各种箭头。

yield 与 JavaScript 中的相同,yield from 是 JavaScript 中的 yield*

要使用 node 0.11 运行使用生成器和 yield 的代码,请使用 --harmony 标志。如果直接使用 lsc 运行,请使用 lsc file.ls --nodejs --harmony 将 harmony 标志传递给 node。

If 和 Unless

有几种方法可以格式化 if 语句。(请注意,if 语句实际上是一个表达式,可以作为表达式使用)。

标准的

if 2 + 2 == 4
  'something'
else
  'something else'

if 2 + 2 == 4 then 'something' else 'something else'




if 2 + 2 == 4
then 'something'
else 'something else'
if (2 + 2 === 4) {
  'something';
} else {
  'something else';
}
if (2 + 2 === 4) {
  'something';
} else {
  'something else';
}
if (2 + 2 === 4) {
  'something';
} else {
  'something else';
}

else 是可选的,可以添加更多 else if

if 2 + 2 == 4
  'something'

if 2 + 2 == 6
  'something'
else if 2 + 2  == 5
  'something else'
else
  'the default'
if (2 + 2 === 4) {
  'something';
}
if (2 + 2 === 6) {
  'something';
} else if (2 + 2 === 5) {
  'something else';
} else {
  'the default';
}

它可以用作表达式

result = if 2 / 2 is 0
         then 'something'
         else 'something else'
var result;
result = 2 / 2 === 0 ? 'something' : 'something else';

它也可以用作后缀 - 它的优先级低于赋值,这使其很有用

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

unless 等效于 if not

unless 2 + 2 == 5
  'something'

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

that 隐式引用条件的值。它将解开存在性检查。

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;
}

循环和推导式

for 循环有三种基本形式。一种遍历数字范围,一种遍历列表中的项目,一种遍历对象的键和值。

我们首先检查遍历数字范围的 for 循环。它的结构为:for (let) (VAR) (from NUM) (to|til NUM) (by NUM) (when COND) - (几乎所有内容都是可选的)。

let 的作用是将循环体包装在一个立即调用的函数表达式中,当您在循环中创建函数并希望函数在调用时循环变量为当前值(而不是最终值)时,这很有用。它也很有用,因为这意味着循环中的变量不会暴露在循环周围的作用域中。

从一个数字开始计数,如果省略,则默认为 0

向上计数 to 并包括一个数字,或向上计数 til(但不包括)一个数字。

by 是步长值,默认为 1

when(别名 case|)是可选的保护。

如果用作表达式,循环将计算为列表。

for i from 1 to 10 by 3
  i
var i$, i;
for (i$ = 1; i$ <= 10; i$ += 3) {
  i = i$;
  i;
}

for ... in 循环遍历列表。它们的结构为:for (let) (VAL-VAR)(, INDEX-VAR) in EXP (by NUM) (when COND) - 同样,几乎所有内容都是可选的。

letbywhen 与之前相同。

VAL-VAR 计算为当前值,而 INDEX-VAR 计算为列表的当前索引。两者都是可选的。

EXP 应计算为数组。

for x in [1 2 3]
  x

xs = for let x, i in [1 to 10] by 2 when x % 3 == 0
  -> i + x
xs[0]! #=> 5
xs[1]! #=> 17
var i$, ref$, len$, x, xs, res$, i;
for (i$ = 0, len$ = (ref$ = [1, 2, 3]).length; i$ < len$; ++i$) {
  x = ref$[i$];
  x;
}
res$ = [];
for (i$ = 0, len$ = (ref$ = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).length; i$ < len$; i$ += 2) {
  i = i$;
  x = ref$[i$];
  if (x % 3 === 0) {
    res$.push((fn$.call(this, i, x)));
  }
}
xs = res$;
xs[0]();
xs[1]();
function fn$(i, x){
  return function(){
    return i + x;
  };
}

for ... of 循环遍历对象。它们的结构为:for (own) (let) (KEY-VAR)(, VAL-VAR) of EXP (when COND) - 同样,几乎所有内容都是可选的。

letwhen 与之前相同。

own 对属性使用 hasOwnProperty 检查,停止原型链中更高层属性的迭代。

KEY-VAR 计算为属性的键,VAL-VAR 计算为属性值。两者都是可选的。

EXP 应计算为对象。

for k, v of {a: 1, b: 2}
  "#k#v"

xs = for own let key, value of {a: 1, b: 2, c: 3, d: 4} when value % 2 == 0
  -> key + value
xs[0]! #=> 'b2'
xs[1]! #=> 'd4'
var k, ref$, v, xs, res$, i$, key, value, own$ = {}.hasOwnProperty;
for (k in ref$ = {
  a: 1,
  b: 2
}) {
  v = ref$[k];
  k + "" + v;
}
res$ = [];
for (i$ in ref$ = {
  a: 1,
  b: 2,
  c: 3,
  d: 4
}) if (own$.call(ref$, i$)) {
  key = i$;
  value = ref$[i$];
  if (value % 2 === 0) {
    res$.push((fn$.call(this, key, value)));
  }
}
xs = res$;
xs[0]();
xs[1]();
function fn$(key, value){
  return function(){
    return key + value;
  };
}

常规嵌套循环将计算为列表的列表。

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;

您可以在 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;
}

列表推导式始终生成列表。嵌套推导式生成扁平化的列表。

[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;
  }
}

您可以使用空格来更好地格式化推导式。

[{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
      });
    }
  }
}

您可以使用级联来隐式引用正在映射的值。

[.. + 1 for [1 2 3]] #=> [2, 3, 4]



list-of-obj =
  * name: 'Alice'
    age: 23
  * name: 'Betty'
    age: 26




[..name for list-of-obj] #=> ['Alice', 'Betty']
var i$, x$, ref$, len$, listOfObj, y$;
for (i$ = 0, len$ = (ref$ = [1, 2, 3]).length; i$ < len$; ++i$) {
  x$ = ref$[i$];
  x$ + 1;
}
listOfObj = [
  {
    name: 'Alice',
    age: 23
  }, {
    name: 'Betty',
    age: 26
  }
];
for (i$ = 0, len$ = listOfObj.length; i$ < len$; ++i$) {
  y$ = listOfObj[i$];
  y$.name;
}

对象推导式生成一个对象。

{[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];
}

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 等效于 while not

while/until 还可以接受 when 保护和一个可选的 else 子句,该子句如果根本没有运行则运行。

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 还可以接受更新子句。

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,并且允许多个条件。

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';
}

如果您切换到没有任何内容,则切换到 true。(它编译为切换到 false,以便它可以使用一个 ! 而不是两个 ! 将 case 转换为布尔值)。

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';
}

您可以使用 fallthrough 停止自动插入 break。它必须是 case 块的最后一个表达式。此外,您可以将 switch 语句用作表达式。

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;

|case 的别名,=>then 的别名。| otherwise| _default 的别名。

switch 'moto'
| "some thing"     => \hello
| \explosion \bomb => \boom
| <[ the moto ? ]> => 'here it is'
| otherwise        => \something
switch ('moto') {
case "some thing":
  'hello';
  break;
case 'explosion':
case 'bomb':
  'boom';
  break;
case 'the':
case 'moto':
case '?':
  'here it is';
  break;
default:
  'something';
}

您可以将 that 与 switch 语句一起使用

switch num
| 2         => console.log that
| otherwise => console.log that
var that;
switch (that = num) {
case 2:
  console.log(that);
  break;
default:
  console.log(that);
}

如果下一个标记是 case,则在箭头 (例如 ->)、:= 后添加隐式 switch

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

func 'hello' #=> lo





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('hello');
state = (function(){
  switch (false) {
  case 2 + 2 !== 5:
    return 'I love Big Brother';
  default:
    return 'I love Julia';
  }
}());

您还可以使用 CoffeeScript 样式的 switch 语句。

day = \Sun
switch day
  when "Mon" then 'go to work'
  when "Tue" then 'go to a movie'
  when "Thu" then 'go drinking'
  when "Fri", "Sat"
      'go dancing'
  when "Sun" then 'drink more'
  else 'go to work'
var day;
day = 'Sun';
switch (day) {
case "Mon":
  'go to work';
  break;
case "Tue":
  'go to a movie';
  break;
case "Thu":
  'go drinking';
  break;
case "Fri":
case "Sat":
  'go dancing';
  break;
case "Sun":
  'drink more';
  break;
default:
  'go to work';
}

赋值

基本赋值与您预期的一样,variable = value,并且不需要变量声明。但是,与 CoffeeScript 不同,您必须使用 := 来修改上层作用域中的变量。

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;

几乎所有内容都是表达式,这意味着您可以执行以下操作

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

诸如循环、switch 语句,甚至 try/catch 语句都是表达式。

如果只想声明一个变量而不初始化它,可以使用 var

var x
var x;

您也可以使用const在LiveScript中声明常量。它们在编译时进行检查 - 编译后的JS没有区别。

尝试编译以下内容

const x = 10
x = 0

导致在第 2 行重新声明常量“x”

但是,如果将对象声明为常量,则不会冻结它们 - 您仍然可以修改其属性。如果使用-k--const标志进行编译,则可以强制所有变量都为常量。

运算符

复合赋值

?||&& 可以作为任何复合赋值的前缀。)

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;

一元赋值

y = \45
+  = y   #=> 45   (make into number)
!! = y   #=> true (make into boolean)
-~-~ = y #=> 3    (intcasting bicrement)
var y;
y = '45';
y = +y;
y = !!y;
y = -~-~y;

赋值默认值 - 您可以使用 ||&&?

您可以在函数参数中使用 = 代替 ?

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();

浸泡赋值 - 仅当右操作数存在时执行赋值

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;

解构

解构是从变量中提取值的一种强大方法。您无需分配给简单变量,而是可以分配给数据结构,从而提取值。例如

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

您还可以使用 splat

[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;

...以及对象!

{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;

您还可以使用 :label 为正在解构的实体命名,以及任意嵌套解构。

[[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;

子结构

轻松设置列表和对象的属性。

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;

属性访问

标准的

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

点访问 - 点运算符可以接受比标识符更多的东西作为其右操作数,包括数字、字符串、括号、方括号和花括号。

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];

使用 .= 进行访问赋值。

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

数组切片和拼接

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 4 by 2] #=> [2,4]
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]];
[list[1], list[3]];
ref$ = [7, 8], list[1] = ref$[0], list[2] = ref$[1];
list;

对象切片

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
});

长度星号 *

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];

半自动赋予 .{}.[] 确保属性作为对象或数组存在。

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;

绑定访问 .~ 将对象的 method 检索为绑定到该对象。使用自动点插入,您只需使用 ~

请注意,这与 Function 上的 .bind 方法不同。使用 .~,方法是动态绑定的;foo~bar 将引用调用绑定函数时 foobar 的值,而不是进行绑定访问时 bar 的值。

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

# Binding access is dynamic:
obj.add = (y) -> @x + 1000*y
target.bound 5     #=> 5005
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);
obj.add = function(y){
  return this.x + 1000 * y;
};
target.bound(5);
function bind$(obj, key, target){
  return function(){ return (target || obj)[key].apply(obj, arguments) };
}

级联

级联始终计算为正在访问的项目,而不是访问操作的返回值。

漂亮的链式调用

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!';

级联是可调用的,并且可以包含任意代码。

console.log
  x = 1
  y = 2
  .. x, y
# prints `1 2` to the console
var x$, x, y;
x$ = console.log;
x = 1;
y = 2;
x$(x, y);

您可以使用 with 指定要级联的前一个表达式的部分。

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;

异常

您可以使用恰如其分命名的 throw 抛出异常。

throw new Error 'an error has occurred!'
throw new Error('an error has occurred!');

可以使用try catch finally块捕获并处理异常。catchfinally都是可选的。

try块会被尝试执行。如果发生异常,则执行catch块。它会接收异常对象作为参数。如果不指定此变量的名称,则默认为e。与JavaScript不同,异常变量的作用域限定在最近的函数内,而不是catch块内。如果需要,可以解构异常对象。

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;

finally块在trycatch之后执行,无论发生了什么。

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();
}

面向对象编程

类是对构造函数定义和原型设置的简单语法糖。

构造函数定义为类定义顶层的函数字面量。

其原型的属性由顶层的对象字面量定义。

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);

静态属性(附加到构造函数的属性)通过在顶层向this添加属性来定义。这些属性可以通过访问constructor(简写@@)在方法中访问。

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();

私有静态属性在类体中定义为普通的变量。(注意:JavaScript和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();

您可以定义绑定方法(使用~>),其this的定义绑定到实例。

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) };
}

您可以使用对象设置参数简写轻松地在构造函数和方法中设置属性。

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;

如果将构造函数定义为绑定函数~>,则在创建新实例时无需使用new

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;

对于更高级别的库和框架,可以通过设置属性constructor$$将构造函数定义为外部函数。不建议在常规编码中这样做。

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;

可以使用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;
}

这在使用super时尤其有用。如果单独使用,super是对应函数的引用。如果要使用所有参数调用它,请使用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;
}

您可以通过implements使用mixin。您只能从一个类继承,但可以混合任意数量的对象。请记住,如果要实现一个类,而不仅仅是一个简单对象,则必须实现类的原型。

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;
}

要修改原型,可以使用简写::,如果要修改多个属性,则可以使用::=运算符。

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();

如果您不想支持旧版浏览器,并且希望使用Object.defineProperty,则可以使用以下简写

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;

CoffeeScript到LiveScript转换指南

  • 将所有胖箭头=>更改为波浪箭头~>
  • 将所有块注释### ###更改为/* */
  • 将所有undefined更改为void
  • 将创建列表的任何范围语法从[x..y]更改为[x to y],并将[x...y]更改为[x til y]。如果您的范围向下,即从较大的数字到较小的数字,并且不明显(from和to不是字面量),则必须添加by -1,例如[x to -3 by -1]
  • 类似地,将for循环中的任何范围语法从for i in [x..y]更改为for i from x to y,并将for i in [x...y]更改为for i from x til y
  • 将任何列表推导式从(x for x in list)更改为[x for x in list]。任何您不希望返回列表的后缀循环,请更改为非后缀,例如,将increase x for x in list更改为for x in list then increase x。如果需要,您可以使用then的别名胖箭头=>来减少字符数
  • 将任何以点开头的数字字面量,例如.5更改为以零开头0.5
  • 将任何正则表达式字面量从/// ///更改为// //
  • 将您使用的任何splat从(args...) ->更改为前缀形式(...args) ->
  • 删除没有参数的函数定义中的括号,() -> 仅变为 -> - 这是因为 () 始终是调用。
  • 将类中构造函数的定义方式从
    class Item
      constructor: ->
    
    更改为在类体顶层简单地定义为函数,例如
    class Item
      ->
    
    如果您的构造函数是外部函数,请使用特殊属性constructor$$定义它。
  • 将对super的任何调用从super更改为super ... - 这是因为super是对父函数的直接引用,而不是调用本身。
  • 将任何按位运算符更改为用点括起来的自身。例如,&现在是.&.>>现在是.>>.
  • 如果在任何时候修改上级作用域中的变量,例如
    x = 10
    do ->
      x = 5
    
    必须使用:=而不是=,因为这将在LiveScript中声明一个新的(遮蔽)变量。因此,如果要修改x,则上述代码需要是
    x = 10
    do ->
      x := 5
    
    如果您想修改x
  • 如果您使用嵌套列表推导式(例如(x + y for x in [1, 2] for y in [3, 4]),则需要将其更改为常规(非后缀)嵌套循环,因为在CoffeeScript中结果是列表中的列表(示例的结果为[[4,5],[5,6]]),而结果是扁平化的(LiveScript中示例的结果为[4,5,5,6])。例如,更改
    result = (x + y for x in [1, 2] for y in [3, 4])
    
    result = for x in [1, 2]
      for y in [3, 4]
        x + y
    
    以获得非扁平化的结果。
  • 更改任何名为itthatfallthroughotherwise的变量的名称。这些都是糟糕的变量名称,因此无论如何您都应该更改它们。从技术上讲,并非在所有情况下都需要这样做,但在所有情况下这样做会减少混淆。it用作没有参数定义的函数的隐式参数,例如reverse = -> it.reverse!that指的是条件的值,例如that + 2 if (x + 10)/(y - 18)that == (x + 10)/(y-18)fallthrough如果在case块的末尾使用,则使该块贯穿到下一个case。otherwise如果直接在case之后使用,则将该case变成默认值。
  • 如果您有任何非三引号字符串的多行字符串(使用"string"'string'),例如
    text = "hi 
            there"
    
    您将不得不像在CoffeeScript中一样更改它们,这将是"hi         there",而LiveScript忽略换行符后的缩进,因此它将是"hi there"
  • andor以及带空格的.?.会关闭隐式调用,因此您必须更改任何依赖于CoffeeScript不关闭调用的代码。例如,f a .g b or h c在CoffeeScript中将是f(a.g(b || h(c))),在LiveScript中将是f(a).g(b) || h(c)。您可以使用||代替or,使用&&代替and,因为这些版本不会关闭隐式调用。
  • 将任何do的特殊情况函数字面量更改为使用let。例如,在CoffeeScript中do ($ = jQuery) -> $更改为
    let $ = jQuery
      $
    
  • 将任何针对以隐式对象开头的块的隐式调用更改为使用do。例如,更改
    f
      a: b
    
    进入
    f do
      a: b
    
  • 将任何JavaScript代码字面量,例如`js code here`更改为``js code here``
  • 更改任何双空格属性访问,例如x . y,使其只在一侧或完全不带空格。现在,双空格点用于组合函数。
  • 将任何不带空格的标识符减法,例如a-b更改为带空格的a - ba-11-b仍然可以使用。这是因为a-b在LiveScript中是一个有效的标识符,等效于其驼峰式写法aB

变更日志

1.6.0

重大更改

  • {a, b, ...c} = a: 1 b: 2 c: 3 x: 4现在将c设置为c: 3 x: 4,而不是c = {} <<< 3。(参见#941。)
  • o{k: a.complex.expression}{a xor b}foo.bar = [a, b]:c{...x ? y} = z现在都是错误。(参见#958了解原因。)
  • a ?+= b现在表示a? && a += b,而不是a? || a += b(参见#969。)

功能

  • 添加了async函数和生成器(#978#1063)
  • 添加了对更复杂的对象简写的支持,例如{a.b!c?key}(#958)
  • 在splat中支持ES6迭代器(#963)
  • 在Node中,添加了一个process.argv.lsc属性,用于可靠地获取解释脚本参数(#1014)

修复

  • 同名化错误(#743#957)
  • 为带标签和柯里化的函数报告错误(#751)
  • 在对象简写中使用时未定义隐式it参数(#899)
  • 分叉错误(#916)
  • for in词法分析错误(#923)
  • match错误(#926)
  • REPL错误(#928#929)
  • 绑定访问和对象切片(#930)
  • 报告源文件行号(#953)
  • 对象切片的各种错误(#958)
  • 编译后的switch代码中不再有无法访问的break(#931)
  • 修复了-ce-aj选项组合(#993)
  • for let ... when保护中的循环变量(#992)
  • 脚本参数回归(#1013)
  • a[]b ++ ca[]b ++= c(#542#1028)
  • 使用-w选项运行时显示错误(#1015)
  • for [] ... else(#1035)
  • 将shebang行移到头部注释上方(#1032)
  • 空单词字面量<[ ]>(#739)
  • for arr case .. in(#1039)
  • 修复了match中的_.member(#1025)
  • 各种let块中的yieldawait(#1019#1021#1023)
  • Windows上的REPL模式(#897)

1.5.0

重大更改

  • [a, ..., b, c, d] = [1 2 3]的LHS现在与[a, ...m, b, c, d]的行为相同。(参见#858。)

  • 一元运算符展开现在需要在要展开的列表前加上...。(参见 #863。)

功能

  • REPL 增加了保存历史文件的特性,并为全局变量提供了自动补全功能 (#695)
  • require 一个 .ls 文件时,会生成嵌入式源映射 (#786)
  • require .json.ls 文件时,会进行适当的处理 (#884)
  • 类成员现在使用 ClassName.prototype.memberName = 模式进行赋值,这允许 JS 优化工具识别它们 (#853)
  • 添加了对 [for x by y] (#837) 和 [.. for from i to j by k] (#859) 的支持

修复

  • 匹配 case 中逻辑表达式的分组 (#584)
  • 绑定二元运算符 (#634)
  • 在 for-let 中使用 yield (#749)
  • Yield 可调用 (#823)
  • 评估带有源映射的 .ls 文件时出错 (#830)
  • Windows 路径错误 (#835)
  • 循环边缘情况(您极不可能遇到)(#841)
  • Heregexes (#844)
  • 使用切片进行自动赋初值 (#847)
  • 嵌套推导式中的解析错误 (#854)
  • splat 参数的性能改进 (#857)

1.4.0

  • 将 npm 名称更改为 livescript,因为 npm 不再允许发布对包含大写字母的包的更新
  • 添加了使用 -m,--map 标记的源映射支持
  • 添加了使用 JSON 和 -e,--eval 标记的命令行 JSON 处理。传入的 JSON 会绑定到 this
  • 修复了先前版本中的一个回归问题,该问题不允许在第一个位置参数之后使用标记样式的位置参数(例如,lsc app.ls --flag 会导致错误)
  • 生成器不再自动静默,行为与预期一致
  • 允许反向调用生成器,例如 *<-
  • 添加了 --const 标记的简写 -k
  • 可以在切片中使用 by 关键字,例如 list[1 til 5 by 2]
  • 对象推导式错误修复
  • 修复了各种反向调用问题
  • 修复了组合运算符的空格问题
  • Yield 错误修复 - 包括允许在没有操作数的情况下使用它
  • 替换了 child_process.spawn 中已弃用的 customFds 用法
  • 添加了 CLI 测试
  • 许多其他错误修复

1.3.1

  • 修复了使用参数调用 lsc file.ls 时的错误 (#569)
  • 一些对象推导式修复
  • 修复了 returnthrow 在管道方面的优先级。
  • 添加了在 1.3.0 中丢失的 --require 选项,此选项在启动 REPL 时非常有用。还以类似于 require! 的方式使模块可访问。例如,--require './boom.js' 需要该文件并允许您使用 boom 访问它。
  • 修复了 require! 编译错误 - 现在会在应该出现错误时抛出错误

1.3.0

  • 添加了对生成器和 yield 的支持
  • 改进了 require!,现在更像普通的解构了
  • 修复了管道和赋值的优先级
  • 删除了自动注入的 IIFE,现在只需使用 for let
  • 浏览器版本更改为使用 browserify
  • 选项解析使用 Optionator
  • 添加了 --no-header 选项,用于删除“Generated by…”标头
  • 从 bin 中删除了 livescript,只需使用 lsc
  • 删除了 Slake 和 Slakefiles
  • 现在可以在 switch 语句的默认 case 中使用 that

1.2.0

  • 错误修复
  • 更好地编译部分应用函数,例如,10 |> f _, 2 在编译时与 f 10, 2 相同
  • 更好地编译组合 - 不再使用辅助函数
  • 使用 prelude.ls 版本 1.0.3
  • --prelude/-d 标记现在仅用于 repl,在编译时不会添加 prelude.ls 包含
  • 生成的标头添加到编译文件的顶部,指定了 JS 使用哪个版本的 LiveScript 编译
  • 由于缺乏实用性和与其他语法的歧义,删除了单行级联
  • 删除了已弃用的功能 - 不存在运算符 !?+++whereundefined(void 的别名)、简短函数语法
  • 要使函数静默(阻止它们自动添加 return 语句),您现在始终可以在箭头前面添加一个 !。例如,(x) !-> x + x
  • 自动编译 .json.ls.json
  • 添加了 for let

1.1.1

  • 修复/关闭 #194#206#150#217#236#238#151#215#213#237#232#161#241
  • 添加了 ++ 作为数组连接运算符,以替换 +++(现在已弃用)。
  • 添加了 ++= 运算符以补充上述内容。
  • 在推导式中添加了快速映射,使用级联引用。例如,而不是执行 [x + 1 for x in xs],您现在可以编写 [.. + 1 for xs].. 是隐式值。请参阅 部分了解更多详细信息
  • 添加了在参数中使用一元运算符的功能。例如,(!x) -> ... 会转换为 (x) -> x = !x; ...。您可以使用它将参数转换为布尔值 (!!x) -> 或数字 (+x) ->。您还可以克隆对象,以便您不会修改原始对象:(^^x) ->。请参阅 部分了解更多信息
  • 绑定柯里化函数 ~~> 现在确实可以工作了,并且部分应用函数,例如 f _, 1 在第一次部分应用时就会绑定。
  • 已弃用 +++ 数组连接运算符,请改用 +++++ 将在下一个主要 LiveScript 版本中删除。
  • 已弃用 where 语句。请改用 let 和/或局部变量。where 将在下一个主要 LiveScript 版本中删除。
  • 已弃用 undefined 作为 void 的别名。请改用 voidundefined 将在下一个主要 LiveScript 版本中删除。
  • 已弃用 !? 不存在运算符。当后缀使用时,请改为否定存在运算符,例如,将 x!? 更改为 not x?。如果用作逻辑运算符,请改为使用 if 语句和存在运算符,例如,将 x !? f! 更改为 f! if x?!? 将在下一个主要 LiveScript 版本中删除。
  • 另请记住,简短函数语法目前也已弃用,并将从下一个主要 LiveScript 版本中删除。例如,f(x) = ... 类型的函数 - 请改用长箭头定义柯里化函数,例如 f = (x) --> ...

1.1.0

  • lsc 现在是 livescript 命令的别名,因为 livescript 比较长。想想“LiveScript Compiler”。
  • 添加了单行级联。例如,[1 2 3]..push 3 ..shift!..sort!
  • 级联是可调用的,例如
    (+)
      .. 2 3
    
  • 删除了之前 with 的用法,改为使用 let @ = x
  • with 可用于指定要级联的表达式的部分。例如
    x = with [1 2 3]
      ..push 3
    
  • 空捕获 try x catch 返回其捕获对象。
  • 在范围明确的地方不需要 by -1
  • 添加了 constructor 的简写:@@
  • 将半自动赋初值从使用 x@yx@@y 更改为使用 x{}yx[]y
  • 嵌套循环(不是推导式)在用作表达式时不再返回扁平化结果。
    res = for x to 2
      for y to 3
        "#x#y"
    res #=> [ ['00', '01', '02', '03'], ['10', '11', '12', '13'], ['20', '21', '22', '23'] ]
    
    如果需要扁平化结果,请改用推导式。为了方便起见,推导式现在可以多行。
    ["#x#y" for x to 2
                  for y to 3]
    #=> ['00', '01', '02', '03', '10', '11', '12', '13', '20', '21', '22', '23']
    
  • 修复了推导式内部的推导式返回扁平化结果的错误,例如
    [[x + y for x to 2] for y to 3]
    #=> [ [0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5] ]
    
    现在按预期返回数组的数组。如果您依赖此行为,只需删除内部推导式的括号即可使其成为嵌套推导式。
  • 添加了 require!,它接受对象、数组或字符串字面量,并创建一系列 require 语句。请参阅 文档
  • 添加了在类体外部调用 super 的功能,用于向类添加静态或实例方法。例如
    class A extends B
    A::meth -> super ...
    A.stat -> super ...
    
  • 范围中的“from”数字可以省略,以与 for 循环保持一致。如果省略该数字,则假定为 0
    [to 3]  #=> [0, 1, 2, 3]
    [til 3] #=> [0, 1, 2]
    
  • 您现在可以在类中定义外部构造函数。这主要用于与 CoffeeScript 兼容,以及用于更高级别的库和框架。它使用 constructor$$: 定义。例如
    class A
      constructor$$: some-external-func
    
  • 您现在可以展开 new,例如 new A ...a
  • 类体现在是可执行的。您可以执行以下操作
    class A
      if condition
        method: -> ...
      else
        method2: -> ...
    
      for key of obj
        (key): -> ...
    
  • 如果您对命名函数执行 do 操作,并且 do 语句不用作表达式,则该函数会保留其提升。例如
    f 1 #=> 1
    do function f x
      x
    
    编译为
    f(1);
    function f(x){
      return x;
    } f();
    
  • 您现在可以从 for 循环定义中省略所有变量以遍历范围。例如
    [1 for til 3] #=> [1, 1, 1]
    for til 3 then f!
    [f! for from 1 to 5]
    
  • 添加了 delete! 语句,类似于 JavaScript 中的 delete。这与 LiveScript 中的 delete 略有不同,后者如果用作表达式则会返回已删除的项。
    delete! x.y
    
  • 添加了 JavaScript 代码字面量,其唯一目的是允许 CoffeeScript 到 LiveScript 的编译兼容性。不要在常规代码中使用。JS 字面量用两个反引号表示。``js code``
  • for 循环和推导式索引是不可变的。如果您想修改索引,请使用 while 循环。此外,索引的最后一个值是它在循环中的最后一个值。
  • 您可以解构 catch 参数。
  • 为了与 CoffeeScript 兼容,in 在稀疏数组上起作用。
  • 错误修复。

1.0.1

  • 错误修复。

1.0.0

  • 组合运算符的更简洁编译
  • --prelude/-d 标记适用于 repl
  • 新的函数语法,例如 f(x) = x 现在使用 ~ 表示绑定函数,类似于命名函数,例如 ~f(x) = x
  • 删除了新函数语法的无返回值选项
  • 添加了类似 Python 的函数关键字参数,例如 f = ({x = 2, y = 3}) -> x + y,然后使用 f y: 4 应用
  • 删除了 + 用于列表连接的重载(只需使用 +++
  • 重新添加了按位复合赋值,例如 .&.=
  • 添加了 where,类似于 let,但位于后面而不是前面 - 也允许嵌套赋值
  • 将模糊等于(JS 中的 ==)更改为 ~=!~= 用于否定
  • 添加了 xor 运算符,异或 - 仅当一侧或另一侧为真时才为真
  • 对正则表达式字面量使用等于运算符将编译为exec,例如:x == /[ae]/g 等价于 /[ae]/g.exec(x) - 如果在if语句中使用,可以使用that获取结果。否定运算符则简单地使用test。
  • 二元逻辑运算符可以调用,例如:(f or g) x 等价于 f(x) || g(x)
  • 允许像CoffeeScript一样进行切片操作,例如:list[1 to x] = [1 2 3]
  • 静态函数现在可以继承,并支持调用super
  • 生成的引用变量现在使用$后缀命名,而不是__前缀。例如:ref$ 而不是 __ref
  • 删除了构造函数简写,只需使用constructor
  • 添加了Coco中的级联运算符..
  • 添加了深比较,允许比较对象、数组等。=== - 实验性功能
  • 添加了match语句 - 实验性功能

0.9.12

  • 更改了位运算符/移位运算符语法,使其更清晰。现在就像JS一样,但用点号括起来。例如:&.&.>>.>>.
  • 速度改进
  • 允许在切片语法中使用任意表达式,例如:list[1 to x]
  • 使类更像CoffeeScript - 详细信息
  • 构造函数现在可以继承
  • 允许空类定义,例如:class A extends B
  • 绑定方法(例如:f: ~>)绑定到实例,而不是类

0.9.11

  • 错误修复
  • 更新以兼容node 0.8.x
  • 添加了使用_占位符的函数部分应用
  • 添加了中缀with "cloneport",克隆头部并将尾部导入其中
  • 允许管道|>和反向管道<|的部分应用,并用作函数
  • 更好地编译各种运算符作为函数
  • 添加了存在性浸透隐式调用/查找,例如:(?length)
  • 允许部分应用赋值 - =+=等。
  • 删除了|>>管道运算符 - 现在使用普通管道和部分应用
  • 将回叫占位符更改为下划线_
  • 添加了下划线_作为switch语句中otherwise的别名
  • 添加了--prelude-d选项,以便在编译后的文件中自动包含prelude.ls
  • 添加了--const-k选项,以便像所有变量都是常量一样进行编译
  • 添加了属性访问器的部分应用,例如:(obj.)
  • 删除了cons运算符&
  • 添加了&作为arguments的别名,允许-> &0 + &1等。
  • 删除了@@作为arguments的别名
  • 提高了管道|>运算符的优先级,以允许在不使用括号的情况下赋值整个内容,而不仅仅是第一部分。
  • 添加了mixin特性implements,感谢Coco,例如:class Cow extends Animal implements Mooer,在类体中执行::<<< Mooer

0.9.10

  • 现在有了`const`和`var`,感谢Coco
  • 隐式访问和调用函数,例如:(.length)(.join \|) - 对于映射/过滤等很有用。
  • 错误修复
  • 改进的repl

0.9.9

  • 各种错误修复
  • 调用柯里化函数且没有参数时,按原样调用而不是返回自身
  • 允许中缀函数的第二个参数部分应用

0.9.8

  • 添加了对象推导式
  • 允许标识符中使用连字符(转换为驼峰命名法)

0.9.7

  • 错误修复
  • 更改了&+++的优先级
  • 更漂亮的管道|>编译

0.9.6

  • 部分应用运算符
  • 运算符作为函数

0.9.5

  • 添加了中缀函数调用
  • 删除了JS字面量
  • 改进的CLI

0.9.4

  • 添加了正确的列表推导式
  • 删除了后缀循环

0.9.3

  • 更改了node要求

0.9.2

  • 改进的CLI
  • 在范围语法中启用了表达式

0.9.1

  • 将数字基数符号更改为~
  • 添加了%%=

0.9.0

  • 初始公开发布,许多更改。有关更多详细信息,请参阅与Coco的差异

有关更多详细信息,请参阅与Coco的差异


灵感来源

  • 一般函数式语言
  • Haskell
  • F#

名称

LiveScript是JavaScript的原始名称之一,因此看起来很贴切。对于那些精通JavaScript的人来说,这是一个内部笑话。

致谢

您可以在此处找到完整的贡献者列表。该列表包括LiveScript及其前身的贡献者。

直接为LiveScript做出贡献的人包括George ZaharievSatoshi MurakamiJoshua WeinsteinJosh PerezPaul MillervendethielkilldreamaudreytclkaoviclibdtinthracklinRaine VirtaDiggory BlakeHaspakersynapsosRafael BelvedereseKara BrightwellRyan HendricksonimpinballskovsgaardPiotr KlibertappedemicGeza KovacsIsiah MeadowsKevin GoslarKyle KirbyRichardRob LoachViacheslav LotsmanovVladislav BotvinYin ZhongAllen HaimBartoszdk00Marek PepkePatrick Kettner

感谢Niels Groot Obbink提供了livescript.org域名。

特别感谢Satoshi,因为这个项目是他项目Coco的一个分支,如果没有它,这个项目是不可能实现的。很高兴能够在他的优秀的Coco编译器基础上进行开发。

贡献指南

Fork LiveScript并进行更改。始终先编写测试(在/test目录中)。

查看包含的Makefile - 它包含所有可用的命令。

始终确保您可以make full - 即构建编译器本身。有用:git checkout -- lib && make full:清理您的lib并编译两次并进行测试。只有在所有测试通过后才发送拉取请求。一旦所有测试通过,重写任何需要重写的编译器,然后make full。使用make test-harmony使用生成器等测试节点的--harmony标志。

不要提交包含由make build-browser创建的构建浏览器文件(browser/livescript.js)的补丁。此文件仅在发布新版本之前构建。

与Coco的差异:详细信息和基本原理

自1.0.x版本以来,本节尚未更新

  • 将Coco和Coke中的所有内容重命名为LiveScript和Slake,并将文件扩展名从.co更改为.ls。选择名称的基本原理:LiveScript是在JavaScript被命名为JavaScript之前JavaScript的名称 - 因此它似乎是一个合适的名称,而且很少有其他项目被命名为LiveScript。Slake是因为lake已被占用,lsake听起来很糟糕。
  • 切换为==编译为===,以及否定运算符。基本原理:大多数人更希望使用JavaScript的===而不是==,而且少打字更好,这使得它与CoffeeScript更相似,CoffeeScript将==编译为===,因此来自CoffeeScript的人需要更改的代码更少。is编译为===保持不变。
  • 切换了inof,使它们像CoffeeScript一样。In遍历值,of遍历键。基本原理:人们需要更改的CoffeeScript代码更少,他们已经习惯了,而且使用in检查一个值是否在一个数组中看起来很合理,使用of感觉很奇怪。
  • 除了~之外的所有位运算符都更改为用点号括起来,例如:&现在是.&.>>.>>.。位赋值运算符(例如:&=)已被删除。基本原理:人们很少一直使用位运算符,它们占用了一些宝贵的符号,这些符号可以用于其他用途。它们仍然可用,只是形式更长。~仍然存在。
  • =>,使用_的管道运算符,已删除。释放=>(用于then别名),(|用作case的别名)。改为使用其他管道(|>)和部分应用。
  • |case(在switch中使用)的别名。基本原理:少打字,看起来不错。以Haskell的守卫为模型。
  • =>then的别名。基本原理:不会鼓励在if语句中使用它,因为它看起来有点奇怪 - 实际上是在switch语句中使用,与|结合使用,以创建一个简洁易懂的结构。基于Haskell在case表达式中使用->。
  • 添加了otherwise和下划线_作为上下文关键字,在case|之后使用时,使其成为默认语句。基本原理:与Haskell(otherwise)相同,下划线与Scala相同 - 并且更短。它允许| otherwise => 4 + 9,这与其余结构相符。
  • ->~>:或赋值之后,当它们后面跟着case标记(case|)时,添加了隐式switch。基本原理:减少了输入量并提高了常见情况下使用switch的美观性,不会增加歧义。
  • 添加了列表连接运算符+++。例如:xs +++ ysxs.concat(ys)。基本原理:少打字,更美观,受Haskell中++函数的启发(必须使用3个加号以避免与增量运算符产生歧义)。
  • ^现在是幂运算符**的别名。基本原理:它可用,并且在其他语言中使用。
  • 幂优先级现在是正确的,幂运算符优先于乘法和除法。它也比一元运算符具有更高的优先级。例如:2*4^2 == 32,而不是Coco中的64。此外,-2^2 == -4。基本原理:数学应该正确工作 - 这是许多语言(包括Haskell)中的做法。
  • 幂运算符现在是右结合的。例如:2^2^3 == 2^(2^3) == 256。基本原理:遵循Haskell和许多其他语言在这方面的做法。
  • 添加了魔法自动柯里化函数,使用-->~~>定义。有了它,您可以执行times = (x, y) --> x * ytimesTwo = times 2timesTwo 4 #=> 8。如果您调用一个柯里化函数且没有参数,它会按原样调用自身,而不是返回自身(如果您想要自身,可以直接引用它)。基本原理:更像Haskell,有用的功能。
  • 添加了新的函数定义语法,例如:add(x, y) = x + y == add = (x, y) --> x + y。您也可以在对象字面量和类定义中使用它,例如:add(x, y): x + y == add: (x, y) --> x + y。您也可以通过以感叹号开头来抑制两者中的返回,例如:!nothingness(params) = true不会返回任何内容。此外,您可以使用id@(param) = something来进行词法绑定this,这相当于id = (param) ~~> something(注意波浪号箭头)。如果您愿意,可以疯狂地执行以下操作:@!func@! = something,这是一个分配给this的函数,它不接受参数也不返回任何内容,同时在词法上绑定到this。使用此语法定义的所有函数都是柯里化的。基本原理:更美观,少打字,更像Haskell。
  • 添加了obj ::= obj2作为obj::<<<obj2的别名。基本原理:似乎是预期的直观行为,看起来更简洁。
  • 添加了yes / on作为true的别名,no / off作为false的别名。添加了undefined作为void的别名,并将isnt作为is not的别名。基本原理:简化从CoffeeScript到LiveScript的过渡,CoffeeScript具有所有这些特性。
  • 添加了when作为case(和|)的别名。基本原理:简化从CoffeeScript的过渡。

  • 允许在使用 case 语句的循环中使用守卫条件,例如 x for x from 1 to 10 when x % 2 is 0。原因:简化从 CoffeeScript 的迁移,并在推导式中像其他语言(Haskell)一样使用守卫条件。
  • 添加了在 switch 中使用 else 作为默认值的功能。原因:简化从 CoffeeScript 的迁移。
  • 添加了 loop 作为 while true 的别名。原因:简化从 CoffeeScript 的迁移。
  • 添加了管道操作符 |>,类似于 F#,val |> func 等价于 func(val)。与柯里化函数结合使用非常有用。原因:有用,如同在 F# 中。
  • 添加了反向管道操作符 <|,类似于 F#:f <| x 等价于 f x。这比看起来更有用,可以帮助你避免使用括号。例如,对于返回一个将单词大写或小写的函数的 toCase 定义,使用 toCase \up <| \hello 而不是 toCase(\up) \hello。原因:有用,如上所述,如同在 F# 中。
  • 添加了类似 F# 的函数组合操作符。>><<(f >> g) x 等价于 g(f(x)),而 (f << g) x 等价于 f(g(x))。原因:非常有用,尤其是在使用柯里化函数时。如同在 F#(以及 Haskell,在那里它是点操作符)中。
  • 添加了 %% 操作符(以及相应的 %%= 操作符),x %% y 等价于 (x % y + y) % y。例如,-3 % 4 == -3; -3 %% 4 == 1。原因:这是其他语言(如 Python 和 Ruby)中 % 操作符的行为方式。
  • 将数字基数格式从 7r4 更改为 7~4。这是因为使用 r 会导致与数字注释产生歧义。例如,36rpm - 它是数字 36 带有数字注释 rpm 还是 pm 基数 36(922)?此外,现在接受任何数字作为基数,以便在该基数不是 2-36 时抛出更好的错误。原因:修复了歧义。
  • 允许在范围语法中使用表达式,例如 [x to y/2] 而不是仅仅使用数字字面量。这具有移除裸范围的副作用,例如 2 to 5 没有包含括号(循环保持不变)。原因:范围中的表达式很有用,比裸范围更有用,裸范围的使用非常有限。也更接近 CoffeeScript,再次简化迁移。
  • 将 CLI 更改为裸编译,以便将定义的变量附加到全局作用域。改进 repl 使其在行以 ->~>= 结尾时继续执行。
  • 添加了列表推导式(例如 [x for x in list when x is \something])并删除了后缀循环(例如 something x for x in list)。推导式始终返回一个列表。嵌套列表推导式的行为正确,即最后一个 for 是最内层循环。原因:遵循 Haskell 和 Python。更一致的行为。更容易在脑海中处理。更直观。
  • 将 JS 字面量更改为使用两个反引号,例如 ``js code here`` 而不是 `js code here`。原因:它很少需要;释放反引号用于中缀函数应用。
  • 添加了中缀函数应用,例如 3 `add` 2。原因:如同在 Haskell 中。这允许对其第二个参数进行部分应用,如操作符,例如 (`times` 2)
  • 将一元克隆操作符从 ^ 更改为 ^^。原因:与幂操作符的部分应用区分开来。
  • 添加了操作符的部分应用。例如,(+ 2) 返回一个将 2 加到其参数上的函数。也允许简单地使用 (*),它是一个将两个参数相乘的函数。(+x)(-x) 仍然表示这些一元操作符的应用 - 操作符必须有空格才能表示该操作符的部分应用。在括号内不再允许使用长度星号 *(有时在设置动态键时使用),因为与部分应用的乘法操作符存在歧义。原因:很棒。如同在 Haskell 中。
  • 添加了一元操作符作为函数。例如,你可以将 (not) 作为函数使用,将其组合等。原因:非常有用,如同在 Haskell 中。
  • 添加了 . 作为组合函数中 << 的别名。这禁止了 x . y 样式的属性访问。原因:如同在 Haskell 中。双空格属性访问不应该被使用。
  • 添加了对象推导式。例如,{[key, val * 2] for key, val of obj} 将返回一个对象,其所有值都是原始对象的两倍。数组字面量的第一部分被转换为键,第二部分被转换为值。原因:在处理对象时非常有用。现在可以将对象映射和过滤回对象。
  • 现在在标识符中支持连字符,它们会被转换为驼峰式大小写。例如,get-room 等价于 getRoomencode-URI 等价于 encodeURI。原因:启用其他用户可能喜欢的不同风格。如同在 Lisp 系列语言中。
  • 添加了隐式调用/查找,例如 (.length) 等价于 -> it.length,而 (.join \*) 等价于 -> it.join \*(obj.) 等价于 -> obj[it]。原因:在映射、过滤等操作时很有用。
  • 添加了中缀 with 操作符(“克隆端口”)。例如,personA = personB with name: \alice 等价于 personA = ^^personB <<< name: \alice。即,它克隆头部并将尾部导入到新对象中。personB 未修改。原因:创建新对象的一种非常好的方法。
  • 添加了部分应用,_ 是占位符。例如,f = add-three-numbers 1, _, 3 然后 f 2 #=> 6。可以多次使用,并且像柯里化函数一样,如果在没有参数的情况下调用,将执行,允许使用默认参数。原因:非常有用,当函数参数的顺序不理想时,可以使用柯里化版本。
  • 将回调用占位符 <- 更改为下划线 _。原因:与部分应用占位符相符。
  • arguments 别名更改为 &,删除了 @@ 作为该别名。例如,-> &0 + &1。原因:更短 - 减少输入。
  • 添加了 -d, --prelude 选项,用于自动添加 prelude.ls。原因:使 prelude.ls 更易于使用。
  • 添加了 -k, --const 选项,用于编译为所有变量都是常量的情况。原因:一些来自所有值都是不可变的语言的用户可能会觉得这个功能不错。
  • 允许在切片语法中使用任意表达式,例如 list[1 to x]。原因:有用。
  • 更改为继承构造函数。原因:有用,并且更像 CoffeeScript。
  • 允许空类定义,即没有块。原因:有用,并且更像 CoffeeScript。
  • 绑定方法绑定到实例,而不是类。原因:绑定到类似乎没有用,因此这更有用。也更像 CoffeeScript。
  • 添加了像 Python 中一样的函数关键字参数。原因:有用。
  • 添加了异或 xor 操作符。原因:逻辑完整性。
  • 二元逻辑是可调用的。原因:有用。
  • 允许像 CoffeeScript 中一样进行拼接。原因:更容易迁移,有用。
  • 在使用正则表达式时重载等于运算符以表示执行/测试。原因:更短。
  • 静态函数被继承。原因:如同在 CoffeeScript 中 - 简化迁移。
  • 删除了构造函数简写。原因:很少有用,使用它进行级联代替。
  • 将级联操作符从 & 更改为 ..。原因:像其他语言中一样。
  • 添加了匹配语句和深度比较。原因:实验性的,看看是否好用。
  • 有关 1.0.0 之后的变化,请参阅 变更日志 部分。