LiveScript 是一种编译成 JavaScript 的语言。它与 JavaScript 具有直接的映射关系,并允许您编写简洁的代码,避免重复的样板代码。虽然 LiveScript 添加了许多功能来辅助函数式编程,但它也对面向对象和命令式编程进行了许多改进。
LiveScript 是 Coco 的分支,并且是 CoffeeScript 的间接后代,它与 CoffeeScript 具有很强的兼容性。
npm install -g livescript
关注 @gkzahariev 以获取 LiveScript 的更新。
特色博文:LiveScript 1.4.0 - 源映射及更多!
双击示例将其加载到编译器/REPL 中。法语文档:Français。
# 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)
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(); }
无嵌套回调和无括号链式调用
<- $ 'h1' .on 'click' alert 'boom!'
$('h1').on('click', function(){ return alert('boom!'); });
您可以通过 npm 安装 LiveScript:sudo npm install -g livescript
。
或者,您也可以下载它(zip,tar.gz),进入其目录,然后运行 sudo make install
。使用 git 下载:git clone git://github.com/gkz/LiveScript.git && cd LiveScript && sudo make install
。Node.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/>
lsc file.ls
- 您可以省略 .ls
。file.js
:lsc -c file.ls
lsc -wc file.ls
lsc -co output src
lsc -wco output src
lsc -bpe '[1 to 5]'
lsc
- 使用 Ctrl-D
退出,使用 Ctrl-J
进行多行输入。您可以通过使用 .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" ] ]
使用 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"
的脚本。
将您自己的项目/公司添加到列表中!
prelude.ls 是使用 LiveScript 时推荐的基本库。它允许您执行以下操作
[1 2 3] |> map (* 2) |> filter (> 3) |> fold1 (+) #=> 10
您可以使用 -d
或 --prelude
选项自动将 prelude.ls 导入到 REPL 中。
Prelude 在此页面上加载,您可以在右侧的编译器/REPL 中运行内容时使用它。
编译时使用 -m, --map
选项生成源映射。它有几个可能的值,none
- 默认值,linked
,linked-src
,embedded
和 debug
生成源映射时涉及三个文件
a
可以选择性地嵌入到 b
中,b
可以选择性地嵌入到 c
中,通过注释。
linked
:不嵌入,c
通过相对路径链接到 b
,b
也链接到 a
linked-src
:b
嵌入到 c
中,但 a
链接到
embedded
:所有内容都嵌入到 c
中
debug
:与 linked 相同,但也会将源节点树的可读表示形式(类似于 ast
选项的输出)输出到 '.map.debug' 文件中。
如果您直接将 lsc 的输出提供给浏览器(即不进行进一步处理),请使用 linked
或 linked-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;
与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);
and
、or
和xor
关闭隐式调用,而||
和&&
则不关闭。
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
检查键是否在对象中。
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 |> f
和f <| 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;
属性复制 - 将可枚举属性从右复制到左,并计算为左。<<<
用于自己的属性,<<<<
用于所有属性。import
和import 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
运算符而不是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!
摆脱这些冗余代码,它接受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);
and
、or
、xor
、带空格的.
或?.
都会关闭隐式调用 - 允许无括号链接。
$ \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
是 (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');
如果您将在支持 async
和 await
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。
您可以在 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
语句。(请注意,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)
- 同样,几乎所有内容都是可选的。
let
、by
和 when
与之前相同。
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)
- 同样,几乎所有内容都是可选的。
let
和 when
与之前相同。
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; } }
自动插入 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
将引用调用绑定函数时 foo
上 bar
的值,而不是进行绑定访问时 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
块捕获并处理异常。catch
和finally
都是可选的。
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
块在try
或catch
之后执行,无论发生了什么。
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;
=>
更改为波浪箭头~>
### ###
更改为/* */
undefined
更改为void
[x..y]
更改为[x to y]
,并将[x...y]
更改为[x til y]
。如果您的范围向下,即从较大的数字到较小的数字,并且不明显(from和to不是字面量),则必须添加by -1
,例如[x to -3 by -1]
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
/// ///
更改为// //
(args...) ->
更改为前缀形式(...args) ->
。() ->
仅变为 ->
- 这是因为 ()
始终是调用。class Item constructor: ->更改为在类体顶层简单地定义为函数,例如
class Item ->如果您的构造函数是外部函数,请使用特殊属性
constructor$$
定义它。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以获得非扁平化的结果。
it
、that
、fallthrough
或otherwise
的变量的名称。这些都是糟糕的变量名称,因此无论如何您都应该更改它们。从技术上讲,并非在所有情况下都需要这样做,但在所有情况下这样做会减少混淆。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"
and
、or
以及带空格的.
和?.
会关闭隐式调用,因此您必须更改任何依赖于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
`js code here`
更改为``js code here``
x . y
,使其只在一侧或完全不带空格。现在,双空格点用于组合函数。a-b
更改为带空格的a - b
。a-1
和1-b
仍然可以使用。这是因为a-b
在LiveScript中是一个有效的标识符,等效于其驼峰式写法aB
。{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)process.argv.lsc
属性,用于可靠地获取解释脚本参数(#1014)it
参数(#899)for in
词法分析错误(#923)match
错误(#926)switch
代码中不再有无法访问的break
(#931)-ce
、-aj
选项组合(#993)for let ... when
保护中的循环变量(#992)a[]b ++ c
、a[]b ++= c
(#542、#1028)-w
选项运行时显示错误(#1015)for [] ... else
(#1035)<[ ]>
(#739)for arr case .. in
(#1039)match
中的_.member
(#1025)let
块中的yield
和await
(#1019、#1021、#1023)[a, ..., b, c, d] = [1 2 3]
的LHS现在与[a, ...m, b, c, d]
的行为相同。(参见#858。)
...
。(参见 #863。)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) 的支持.ls
文件时出错 (#830)livescript
,因为 npm 不再允许发布对包含大写字母的包的更新-m,--map
标记的源映射支持-e,--eval
标记的命令行 JSON 处理。传入的 JSON 会绑定到 this
lsc app.ls --flag
会导致错误)*<-
--const
标记的简写 -k
by
关键字,例如 list[1 til 5 by 2]
child_process.spawn
中已弃用的 customFds
用法return
和 throw
在管道方面的优先级。--require
选项,此选项在启动 REPL 时非常有用。还以类似于 require!
的方式使模块可访问。例如,--require './boom.js'
需要该文件并允许您使用 boom
访问它。require!
编译错误 - 现在会在应该出现错误时抛出错误require!
,现在更像普通的解构了for let
--no-header
选项,用于删除“Generated by…”标头livescript
,只需使用 lsc
that
10 |> f _, 2
在编译时与 f 10, 2
相同--prelude/-d
标记现在仅用于 repl,在编译时不会添加 prelude.ls 包含!?
、+++
、where
、undefined
(void 的别名)、简短函数语法!
。例如,(x) !-> x + x
.json.ls
到 .json
for let
++
作为数组连接运算符,以替换 +++
(现在已弃用)。++=
运算符以补充上述内容。[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
的别名。请改用 void
。undefined
将在下一个主要 LiveScript 版本中删除。!?
不存在运算符。当后缀使用时,请改为否定存在运算符,例如,将 x!?
更改为 not x?
。如果用作逻辑运算符,请改为使用 if 语句和存在运算符,例如,将 x !? f!
更改为 f! if x?
。!?
将在下一个主要 LiveScript 版本中删除。f(x) = ...
类型的函数 - 请改用长箭头定义柯里化函数,例如 f = (x) --> ...
。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@y
和 x@@y
更改为使用 x{}y
和 x[]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 ...
0
。[to 3] #=> [0, 1, 2, 3] [til 3] #=> [0, 1, 2]
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();
[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
``js code``
for
循环和推导式索引是不可变的。如果您想修改索引,请使用 while
循环。此外,索引的最后一个值是它在循环中的最后一个值。catch
参数。in
在稀疏数组上起作用。--prelude
/-d
标记适用于 replf(x) = x
现在使用 ~
表示绑定函数,类似于命名函数,例如 ~f(x) = x
f = ({x = 2, y = 3}) -> x + y
,然后使用 f y: 4
应用+
用于列表连接的重载(只需使用 +++
).&.=
where
,类似于 let
,但位于后面而不是前面 - 也允许嵌套赋值==
)更改为 ~=
,!~=
用于否定xor
运算符,异或 - 仅当一侧或另一侧为真时才为真x == /[ae]/g
等价于 /[ae]/g.exec(x)
- 如果在if语句中使用,可以使用that
获取结果。否定运算符则简单地使用test。(f or g) x
等价于 f(x) || g(x)
list[1 to x] = [1 2 3]
super
$
后缀命名,而不是__
前缀。例如:ref$
而不是 __ref
constructor
..
===
- 实验性功能match
语句 - 实验性功能&
为 .&.
,>>
为 .>>.
list[1 to x]
class A extends B
f: ~>
)绑定到实例,而不是类_
占位符的函数部分应用with
"cloneport",克隆头部并将尾部导入其中|>
和反向管道<|
的部分应用,并用作函数(?length)
=
、+=
等。|>>
管道运算符 - 现在使用普通管道和部分应用_
_
作为switch语句中otherwise
的别名--prelude
或-d
选项,以便在编译后的文件中自动包含prelude.ls--const
或-k
选项,以便像所有变量都是常量一样进行编译(obj.)
&
&
作为arguments
的别名,允许-> &0 + &1
等。@@
作为arguments
的别名|>
运算符的优先级,以允许在不使用括号的情况下赋值整个内容,而不仅仅是第一部分。implements
,感谢Coco,例如:class Cow extends Animal implements Mooer
,在类体中执行::<<< Mooer
。(.length)
和(.join \|)
- 对于映射/过滤等很有用。&
和+++
的优先级|>
编译~
%%=
有关更多详细信息,请参阅与Coco的差异。
您可以在此处找到完整的贡献者列表。该列表包括LiveScript及其前身的贡献者。
直接为LiveScript做出贡献的人包括George Zahariev、Satoshi Murakami、Joshua Weinstein、Josh Perez、Paul Miller、vendethiel、killdream、audreyt、clkao、viclib、dtinth、racklin、Raine Virta、Diggory Blake、Haspaker、synapsos、Rafael Belvederese、Kara Brightwell、Ryan Hendrickson、impinball、skovsgaard、Piotr Klibert、appedemic、Geza Kovacs、Isiah Meadows、Kevin Goslar、Kyle Kirby、Richard、Rob Loach、Viacheslav Lotsmanov、Vladislav Botvin、Yin Zhong、Allen Haim、Bartosz、dk00、Marek Pepke、Patrick 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
)的补丁。此文件仅在发布新版本之前构建。
自1.0.x版本以来,本节尚未更新
==
编译为===
,以及否定运算符。基本原理:大多数人更希望使用JavaScript的===
而不是==
,而且少打字更好,这使得它与CoffeeScript更相似,CoffeeScript将==
编译为===
,因此来自CoffeeScript的人需要更改的代码更少。is
编译为===
保持不变。in
和of
,使它们像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 +++ ys
为xs.concat(ys)
。基本原理:少打字,更美观,受Haskell中++函数的启发(必须使用3个加号以避免与增量运算符产生歧义)。^
现在是幂运算符**
的别名。基本原理:它可用,并且在其他语言中使用。-->
和~~>
定义。有了它,您可以执行times = (x, y) --> x * y
、timesTwo = times 2
、timesTwo 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的过渡。
x for x from 1 to 10 when x % 2 is 0
。原因:简化从 CoffeeScript 的迁移,并在推导式中像其他语言(Haskell)一样使用守卫条件。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 >> 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,再次简化迁移。->
、~>
或 =
结尾时继续执行。[x for x in list when x is \something]
)并删除了后缀循环(例如 something x for x in list
)。推导式始终返回一个列表。嵌套列表推导式的行为正确,即最后一个 for 是最内层循环。原因:遵循 Haskell 和 Python。更一致的行为。更容易在脑海中处理。更直观。``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
等价于 getRoom
,encode-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]
。原因:有用。xor
操作符。原因:逻辑完整性。&
更改为 ..
。原因:像其他语言中一样。