CoffeeScript 2.7 入门指南:这门"编译为 JavaScript 的小语言"及其安装、编译与使用原理
【免费下载链接】coffeescriptUnfancy JavaScript项目地址: https://gitcode.com/gh_mirrors/co/coffeescript
CoffeeScript 是一门编译为 JavaScript 的小语言,本指南以 documentation/sections/introduction.md 为骨架,带你理解其"不过是 JavaScript"(It's just JavaScript)的设计哲学,掌握本地/全局安装、命令行执行与编译、语言速览,并结合 src/coffeescript.coffee 与 src/index.coffee 源码,深入其词法分析→语法分析→代码生成的编译管线,以及 Node.js API 与浏览器端的完整使用方式。
CoffeeScript 是什么:一门编译进 JavaScript 的小语言
官方定位(见 introduction.md)非常精炼:CoffeeScript is a little language that compiles into JavaScript——它是一门"小语言",最终产物是 JavaScript。
这一表述背后有两层含义:
- 语言足够小:语法刻意精简,去掉 JavaScript 中繁琐的样板(样板代码),只保留表达力最强的核心结构;
- 编译是唯一运行方式:CoffeeScript 源码从不被解释器直接执行,而是先被完整编译成等价的 JavaScript,再由 JavaScript 引擎运行。
在该语言的自我描述中,JavaScript 被比喻为"外表粗糙(awkward Java-esque patina),内里有一颗华丽的心(gorgeous heart)"。CoffeeScript 的使命,就是把 JavaScript 中真正优雅的部分用一种更简单的方式暴露出来——去掉样板、简化语法,但不改变底层语义。
"It's just JavaScript":黄金法则与三大承诺
CoffeeScript 的黄金法则是"It's just JavaScript"(不过是 JavaScript)。围绕这条法则,官方文档给出了三项可以直接验证的承诺:
一对一编译,无运行时解释CoffeeScript 代码逐条编译为等价的 JavaScript,运行时不存在任何"解释器"层。这也意味着不需要引入庞大的运行时库——编译产物就是纯 JS。
与现有 JavaScript 生态完全互通你可以从 CoffeeScript 中无缝使用任何已有的 JavaScript 库(反之亦然)。因为编译产物就是普通 JS,模块边界、函数调用、对象传递都不存在语言隔离。
输出可读、美观,且性能不逊手写 JS编译输出经过 pretty-print 格式化,可读性良好;由于几乎是一对一的机械转换,生成代码的运行速度"倾向于与等价的手写 JavaScript 一样快或更快"。这一点与许多"运行时转译"语言有本质区别——没有运行时开销,性能损耗只可能来自代码生成的质量,而一对一转换保证了这一点。
从源码看,这种"无运行时"设计贯彻得很彻底:src/coffeescript.coffee 的编译器产出的就是纯文本 JavaScript(js += fragment.code),产物中不注入任何框架代码;唯一可选的"依赖"是--transpile时借用的 Babel,但那属于可选的旧语法适配层,而非语言自身的运行时。
环境要求与安装:本地安装与全局安装
CoffeeScript 的命令行版本以 Node.js 工具的形式发布(详见 documentation/sections/installation.md),当前仓库的 package.json 声明了:
- 当前版本:
2.7.0 - 运行环境:
"node": ">=6"(Node 6 及以上) - 命令行入口:
bin下提供coffee与cake两个命令
安装前请确保已安装 Node.js(建议使用最新的稳定版本)。安装有两种方式,对应两种使用场景:
方式一:本地安装(推荐用于具体项目)
npm install --save-dev coffeescript在项目目录内执行。这样 CoffeeScript 的版本会作为该项目的开发依赖被记录,不同项目可以锁定不同版本,互不干扰。
方式二:全局安装(便于随处执行.coffee文件)
npm install --global coffeescript这会全局提供coffee和cake两个命令。
一个值得注意的版本解析细节:coffee与cake命令会先在当前目录查找本地安装的 CoffeeScript,找到就用本地的,找不到才用全局的。因此全局与本地可以共存不同版本,项目内执行命令时以本地版本为准。
关于--transpile的额外依赖:如果计划使用--transpile选项(参见 documentation/sections/transpilation.md),还需要额外安装@babel/core——全局安装的 CoffeeScript 对应全局安装 Babel,本地安装的对应本地安装 Babel。
快速开始:执行脚本与编译脚本
安装完成后,即可通过coffee命令使用(见 README.md)。
直接执行一个 CoffeeScript 脚本:
coffee /path/to/script.coffee把 CoffeeScript 编译为 JavaScript 文件:
coffee -c /path/to/script.coffee-c(compile)会把.coffee源码编译为同名的.js文件,编译产物即纯 JavaScript,可直接被 Node.js 或浏览器加载。
从源码看,编译入口对以下文件扩展名生效(src/coffeescript.coffee):
exports.FILE_EXTENSIONS = FILE_EXTENSIONS = ['.coffee', '.litcoffee', '.coffee.md']其中.litcoffee与.coffee.md是"文学化 CoffeeScript"(Literate CoffeeScript)的文件形式,说明编译器对文档型源码同样原生支持。
语言速览:用一段示例看懂核心语法
要快速理解"小语言"到底长什么样,最直接的素材是官方示例 documentation/examples/overview.coffee。它用十几行代码覆盖了 CoffeeScript 的主要语法面:
# Assignment: number = 42 opposite = true # Conditions: number = -42 if opposite # Functions: square = (x) -> x * x # Arrays: list = [1, 2, 3, 4, 5] # Objects: math = root: Math.sqrt square: square cube: (x) -> x * square x # Splats: race = (winner, runners...) -> print winner, runners # Existence: alert "I knew it!" if elvis? # Array comprehensions: cubes = (math.cube num for num in list)对照 documentation/sections/language.md 的语法基础说明,可以提炼出这套语法的几个支柱:
- 用缩进代替花括号:函数、if、switch、try/catch 等代码块一律用缩进界定,不再需要
{ }。 - 用换行代替分号:表达式以换行结尾即终止,无需
;(同一行内塞多个表达式时仍可用分号分隔)。 - 省略调用括号:传参调用时无需括号,隐式调用会向后延伸到行尾或块表达式末尾。例如
console.log sys.inspect object会编译为console.log(sys.inspect(object));。 - 后置条件与后置循环:
number = -42 if opposite、math.cube num for num in list这类把条件/循环后置的写法,让代码读起来更接近自然语言。 ->定义函数、...表示 splats(可变参数)、?表示存在性判断(elvis?检查变量是否已定义且非 null/undefined)。
示例中cubes = (math.cube num for num in list)是数组推导式,编译后等价于一段循环 push 的 JavaScript——这正是"编译为等价 JS"的直观体现。
编译原理:从源码看词法、语法与代码生成
"一对一编译、无运行时"并非口号,src/coffeescript.coffee 中的compile函数完整呈现了编译管线:
tokens = lexer.tokenize code, options # ... nodes = parser.parse tokens # ... fragments = nodes.compileToFragments options # ... js += fragment.code整个流程分为四步:
- 词法分析(Lexer):
lexer.tokenize把源码字符串切成 token 流。期间还会收集源码中所有IDENTIFIERtoken 作为referencedVars,让编译器生成临时变量时避开用户已有的名字,避免命名冲突。 - 语法分析(Parser):
parser.parse基于 Jison 生成的文法(src/grammar.coffee)把 token 流规约为 AST。解析时会检测IMPORT/EXPORT语句——一旦出现,自动强制bare模式(见下文),因为模块语法本身就需要顶层作用域。 - 代码生成(Nodes):
nodes.compileToFragments把 AST 编译为一系列代码片段(fragments),每个片段携带原始源码的位置信息(locationData)。 - 拼接输出:把片段按顺序拼接成最终 JS 字符串,并同步计算行列偏移,为生成 source map 做准备(见 src/sourcemap.litcoffee)。
语法错误的报错也做得相当细致:parser.yy.parseError会针对"意外的缩进""意外的标识符""非预期的输入结尾"等场景生成专门的错误信息,并带上精确的位置。
Node.js API:compile、run、eval 与 register
除命令行外,CoffeeScript 还提供完整的 Node.js API(详见 documentation/sections/nodejs_usage.md 与 src/index.coffee)。
注册扩展名,直接requireCoffeeScript 文件:
require 'coffeescript/register' App = require './app' # .coffee 扩展名可省略register让 Node 的模块加载器认识.coffee等扩展名,之后require './app.coffee'与require './app'均可工作。
编译字符串:
CoffeeScript = require 'coffeescript' eval CoffeeScript.compile 'console.log "Mmmmm, I could really go for some #{Math.pi}"'compile(code, options)的核心选项(与 CLI 选项一一对应):
| 选项 | 类型 | 作用 |
|---|---|---|
sourceMap | boolean | 为 true 时生成 source map,返回值从字符串变为{js, v3SourceMap, sourceMap}对象 |
inlineMap | boolean | 为 true 时把 source map 以 base64 编码字符串内联到输出底部注释中 |
filename | string | 用于 source map 的文件名,可含相对或绝对路径 |
bare | boolean | 为 true 时不输出顶层函数安全包装(IIFE) |
header | boolean | 为 true 时在输出顶部加入Generated by CoffeeScript头注释 |
transpile | object | 传入要交给 Babel 的选项对象,见 documentation/sections/transpilation.md |
ast | boolean | 为 true 时返回输入源码的抽象语法树(AST) |
执行与求值:
CoffeeScript.run(code, options):编译并执行一段 CoffeeScript,会正确设置__filename、__dirname与相对require()的解析路径(src/index.coffee)。CoffeeScript.eval(code, options):在类似 Node 的沙箱环境中编译求值,CoffeeScript 自带 REPL(src/repl.coffee)即基于此实现。
值得一提的是transpile的实现细节:Node API 会向编译器注入对 Babel 的引用(options.transpile.transpile = CoffeeScript.transpile),这样像 Webpack 这样的构建工具require('coffeescript')时不会被强制要求安装 Babel(见 src/index.coffee)。
在浏览器中运行与深入学习路径
CoffeeScript 的核心编译器不依赖 Node,可以在任意 JavaScript 环境中运行(documentation/sections/installation.md)。仓库为此提供两份浏览器专用构建:
- lib/coffeescript-browser-compiler-modern/coffeescript.js:面向现代浏览器;
- lib/coffeescript-browser-compiler-legacy/coffeescript.js:面向旧式浏览器。
package.json 中通过module与browser字段分别指向这两份构建,方便打包工具按环境自动选择。
进一步学习可以沿以下仓库资源展开:
- 完整语言参考:documentation/sections/language.md;
- 交互式在线体验与站点示例:documentation/site/;
- 编译器源码:词法 src/lexer.coffee、文法 src/grammar.coffee、AST 节点 src/nodes.coffee、source map src/sourcemap.litcoffee;
- 大量可运行的语法示例:documentation/examples/。
小结
CoffeeScript 的立身之本可以浓缩为三句话:它是编译为 JavaScript 的小语言;它的黄金法则是"It's just JavaScript",即一对一编译、无运行时、生态互通;它的安装与使用都极其轻量(npm install后用coffee执行或编译)。从本仓库源码可以确认,这条"无运行时、纯编译"的路线贯彻到了编译器每一层——从 lexer 到 parser 再到代码片段拼接,最终产出的始终是干净、可读、可直接运行的 JavaScript。
【免费下载链接】coffeescriptUnfancy JavaScript项目地址: https://gitcode.com/gh_mirrors/co/coffeescript
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考