[node.js能做什么]node.js结合coffeescript使用方式汇总教程

更新时间:2019-12-17    来源:php入门    手机版     字体:

【www.bbyears.com--php入门】

其实coffeescript这种语言因其可以一对一地翻译为javascript的特性,使用起来其实非常灵活。 将其引入项目的方式也不止一个。这里,我先就node项目引入coffeescript的方式作一个汇总,并对比一下各个方式的优劣性。


直接使用coffee指令运行纯coffeescript项目

一般提起coffeescript,自然而然地会想到他是javascript的小弟,总脱离不了js的阴影。其实你完全可以把它认作是独立的语言。 我们都知道,在node平台上全局安装完coffee-script包后,就可以通过coffee指令进入coffeescript的交互界面, 叫它repl也行。如果你的项目完全是用coffee写的,那就简单了,直接对你的入口脚本使用coffee指令就结了, 比如你的入口脚本名为“app.coffee”,那就执行:

coffee app.coffee

注意,这里的扩展名coffee是不能省略的。

这个方式应该说是使用coffeescript最“官方”的方式。简单,直接!而且,一旦你以一个coffee文件作为项目的入口, 那整个项目就同时兼容coffee和js了。你在项目里可以任意require js或coffee文件及模块, 甚至可以在项目中的js文件中随便require coffee文件。并且在你引用无论是coffee还是js文件的时候都无需扩展名, 只要前面部分名称不冲突就行。

这个方式有个最大的问题就是,如果它作为一个模块,只能被用于coffee项目;如果他作为一个应用, 运行环境必须安装coffee-script。毕竟coffeescript现在还是一个小众语言,它作为模块时丧失了js用户实在可惜。

另一个也许存在的缺点是性能方面的,毕竟node里面只有js引擎,coffee代码需要先编译为js再运行, 这个过程是要消耗一点点时间的,尽管coffee到js的编译速度其实挺快的。不过这应该不是什么大问题, 一般来说,require都是写在文件的顶部,也就是应用在启动的时候就一气儿把该require的文件都require了, require的时候coffee就被编译成了js放到了js引擎中,那么编译消耗的那点时间都集中在了应用启动时, 运行时几乎不会遇到require新的coffee的情况了。node最常见的使用场景是web服务器,这就更没问题了。


在javascript项目中引用coffeescript

npm中的coffee-script既可以全局安装,也可以作为项目的一个模块安装。那coffee-script作为项目的一个模块有啥意义呢? 其实是给项目添加了一个coffeescript的编译器,这个项目就可以在运行时随时编译coffee文件。

你一定希望像第一种方式里那样随便引用coffee文件。没问题,只需要注册一下。假如你的项目入口文件是app.js, 那么只需要在这个文件最前面加上这么一句:

require('coffee-script/register');

然后你就可以在项目中随便require coffee文件了。

这个方式本质上和第一种方式没啥区别,只不过coffee-script没安装在全局,因此你的模块可以独立存在, 作为应用也不需要环境安装好coffee-script了。

缺点嘛,我觉得最大的问题就是容易让代码有些乱,一会儿js,一会儿coffee,当然第一种方式也可能会这样, 不过都用coffee启动了里面应该不会写js了吧……总之我觉得一个项目还是把语言统一起来比较好 (遗憾的是我主要用这种方式,在一个已经用js写出了大体结构的项目里,我就想用coffee肿么办……)

性能问题上跟第一种方式一样,不多说了。


正统的方式――编译

一说编译,就感觉回到了正儿八经的C或Java的时代。的确,作为一个编译型语言,编译后再运行才是正道。 c有gcc,java有javac,cofee有coffee -c。

要编译一个cofee文件很简单,比如要编辑app.coffee这个文件,就在文件的当前目录执行:

coffee -c app.coffee

一个名为app.js的文件就出现在当前目录下了。这个指令也可以应用于目录, 比如你把项目中所有的coffee源文件放到了src目录下,那就执行:

coffee -c src

src目录及其各级子目录下的所有coffee源文件都会编译成js文件,放到和源文件相同的目录中。

不过对于大型项目,把源文件和编译结果文件放到一起可不太好。指定一个输出目录就行了:

coffee -c -o outputs src

这个指令的参数顺序有点奇怪。在coffee的帮助里是这么定义的:

coffee [options] path/to/script.coffee -- [args]

注意,所有的选项(options)都在coffee和文件路径之间。而最后的args是把目标文件作为脚本执行时给传递的参数。 也就是说所有的选项都放在coffee和文件名之间就可以了。 而-c这个选项是单独的,没有自己的参数,它只表示要把指令最后面提供的那个文件给编译了,所以写成这样也行:

coffee -o outputs -c src

假如想再加个选项,让编译结果不被自执行函数体包围,就是:

coffee -o outputs -c -b src

再假如想把所有源文件编译成一个名为out.js的目标文件,就是:

coffee -o outputs -c -j out src

如果每次改点代码都要这么执行指令也挺烦人的。coffee指令有一个选项-w可以监视源文件的变动而自动编译:

coffee -o outputs -c -w src

对于大型项目来说,最好提前确定好编译方式,让所有开发人员只需要一个指令就搞定所有编译的事情,这就需要自动化构建了。

offee提供了一个自动化构建工具,cake,就像c世界的make。 不过就像官网上说的那样,cake是一个很简单的构建系统。实际上cake的功能就是执行一个名为cakefile的脚本, 而cakefile脚本是用coffeescript写的。这个脚本只提供非常有限的内建函数,比如task, 用于声明一个指令及其对应的描述和执行函数。其它的就是在写一个纯粹的node项目, 想完成编译要么使用node的fs模块输出coffee模块编译出来的字符串, 要么用child_process模块执行shell指令。其实cake构建的目标不一定必须是coffee,由于它实际是执行一个node脚本, 处理任何自动化的事情都可以。

另外还有一些更优秀的第三方自动化构建工具也可以完成coffee的自动编译,比如著名的Grunt,以及国内的fekit等。

这种正统的编译方式也许是看起来最可靠的,应该深受老程序员的喜爱。它可以让团队形成固定的开发模式。 另外,编译后的项目就成了纯的js项目,无论是作为应用直接运行还是作为模块被别的项目引用都不需要额外的依赖。 并且在运行时不需要编译,也就完全不存在编译导致的性能问题了。

缺点嘛,就是太麻烦。如果你是要做一个不太大的项目,光搞cakefile或者配置grunt就要费半天时间,不太值得。



利用 CoffeeScript 和 Node 进行函数式 JavaScript 编程


函数式脚本编写掌控了 Web 应用程序的复杂性

CoffeeScript 在填补 JavaScript 不完善之处方面声名卓著,但它还有其他一些值得探索的优势。在本文中,Andrew Glover 将向您展示 CoffeeScript 较为整洁的语法如何使您能够更轻松地利用 JavaScript 库中的函数式结构,特别是用于 Node.js 中的服务器端编程。作者最后使用了一系列简短的演示,展示如何使用 JavaScript 的实用工具库 Underscore.js 来处理 CoffeeScript 和 Node 中的集合。

CoffeeScript 是一种相对较新的语言,为开发人员提供了不再有 JavaScript 缺陷的令人期待的方案。利用 CoffeeScript,开发人员即可使用一种轻量级、直观的语言完成编码工作,这种语言就像是 Ruby 和 Python 的混合体。对于兼容浏览器的 Web 应用程序,CoffeeScript 将编译为 JavaScript;对于服务器端应用程序来说,它还能与 Node.js 无缝地协同工作。本文的核心是使用 CoffeeScript 的第三项收益,也就是处理 JavaScript 的函数 方面的功能。CoffeeScript 拥有整洁、现代化的语法,释放了 JavaScript 库中潜藏的函数式编程世界。


主流编程语言中的函数式编程

尽管没有任何一种主流编程语言(例如 Java™ 语言、C++ 和 C#)明确作为函数式编程语言,但这些语言中的附加库和框架实现了各种级别的函数式编程。更重要的是,像 Clojure、F# 和 Erlang 这样的语言日益趋向主流,因为函数式编程所产生的 bug 更少,而且能提高复杂应用程序的生产力。

与 JavaScript 相似,函数式编程同样非常有用,但也是一段时间非常不受欢迎。JavaScript 最初被视为一种玩具式的语言,而函数式编程则因超高的复杂度而闻名。但随着对高度并发式应用程序的需求增加,人们急需找到一种替代方法来取代现有编程风格。事实证明,函数式编程并不存在传闻中的不必要的复杂性,它是一款出色的工具,能够整理某些类型的应用程序中固有的复杂性。

在这篇文章中,我们将探讨如何利用名为 Underscore 的 JavaScript 库在 CoffeeScript 和 Node 中进行函数式脚本编程。将这三项技术结合,就会构成一种强大的技术体系,使您能利用 JavaScript,开发出运用函数式编程的服务器端和基于浏览器的应用程序。

设置 CoffeeScript 和 Node

如果您的开发环境中已经安装了 Node.js,那么您可以直接使用它的包管理器 (NPM) 来安装 CoffeeScript。以下命令将告知 NPM 在全局安装包:

 $> npm install -g coffee-script

使用 CoffeeScript 时,您的大部分时间将花费在编写程序、将其保存为 .coffee 文件、然后将结果编译为 JavaScript 方面。CoffeeScript 的语法与 JavaScript 语法极为接近,因此大多数开发人员都能轻松上手;举例来说,清单 1 中的 CoffeeScript 脚本与 JavaScript 极其相似,只是没有 JavaScript 中常见的那种混乱的括号和分号:
清单 1. 典型的 CoffeeScript

 $> coffee -bpe "console.log 'hello coffee'"    console.log('hello coffee');

coffee 命令是执行某些管理任务的捷径。它能够将 CoffeeScript 文件编译为 JavaScript、运行 CoffeeScript 文件,甚至可以作为一种交互式环境或者 REPL(类似于 Ruby 的 irb)。

下面,我将我的脚本存到一个文件中:

 console.log "hello coffee"

随后我将这个文件编译(或转换)为 JavaScript:

 $> coffee -c hello.coffee

结果获得了一个名为 hello.js 的文件。由于所得到的 JavaScript 脚本对于 Node 同样有效,因此我可以直接在我的 Node 环境中运行它:
清单 2. 在 Node 中运行 JavaScript

 $> node hello.js  hello coffee!

此外,我还可以使用 coffee 命令来运行原始的 .coffee 文件,如清单 3 所示:
清单 3. 在 Node 中运行 CoffeeScript

 $> coffee hello.coffee  hello coffee!

注意观察监控器工具 - watchr

开放源码社区制作了大量便捷的文件监控器实用工具,能够完成运行测试、编译代码等任务。这些工具通常是通过命令行工作的,属于极为轻量级的工具。我们将配置监控器工具,用它来监控我们的开发环境中的所有 .coffee 文件,并在保存时将其编译为 .js 文件。

在实现这个目标时,我喜欢使用的实用工具是 watchr,这是一个 Ruby 库。为了使用 watchr,您的开发环境中需要安装 Ruby 和 RubyGems。在安装完成之后,即可运行以下命令,将 watchr 安装为全局 Ruby 库(包括相应的实用工具):

 $> gem install watchr

在 watchr 中,您使用正则表达式定义要监视的文件,以及应该对其执行的操作。以下命令将 watchr 配置为编译在 src 目录中找到的全部 .coffee 文件:

 watch('src\/.*\.coffee') {|match| system "coffee --compile --output js/ src/"}

请注意,本例中的 coffee 命令会将所得到的 .js 文件置于一个 js 目录内。

我可以在一个终端窗口中触发这项操作,例如:

 $> watchr project.watchr

现在,只要我对 src 目录中的任何 .coffee 文件作出修改,watchr 都能确保创建一个新的 .js 文件,并将其放置在我的 js 目录中。


CoffeeScript 概览

CoffeeScript 引入了多种极有价值的特性,因此使用起来比 JavaScript 更容易。CoffeeScript 大体上消除了使用花括号、分号和 var 关键字、function 关键字的需要。实际上,我最喜爱的 CoffeeScript 特性之一就是它的函数 定义,如清单 4 所示:
清单 4. CoffeeScript 函数非常简单!

 capitalize = (word) ->
  word.charAt(0).toUpperCase() + word.slice 1

console.log capitalize "andy" //prints Andy

这里,我在 CoffeeScript 中声明了一个简单的函数,将某个词的首字母大写。在 CoffeeScript 中,函数定义的语法紧接一个箭头之后。主体部分也是使用空格分隔的,因此 CoffeeScript 没有花括号。另外还要注意这里没有使用圆括号。CoffeeScript 的 word.slice 1 将编译为 JavaScript 的 word.slice(1)。同样,请注意函数的主题部分也是使用空格分隔的:函数定义行下的所有代码均缩排。下方未缩排的 console.log 表示方法的定义已完整。(CoffeeScript 的这两项特性分别借鉴自 Ruby 和 Python。)

您可能希望了解对应的 JavaScript 函数是怎样的,清单 5 就给出了对应的 JavaScript 代码:
清单 5. 即便是 JavaScript 的单行代码也是非常复杂的

var capitalize = function(word) {
  return word.charAt(0).toUpperCase() + word.slice(1);
};

console.log(capitalize("andy"));

变量

CoffeeScript 能自动在您定义的任何变量之前添加 JavaScript 形式的var。因此,在 CoffeeScript 中编写代码时,您不需要牢记 var。(JavaScript 中的 var 关键字是可选的。如果没有这个关键字,您的变量将成为全局变量,而这种做法在绝大多数情况下都是不合理的做法。)

CoffeeScript 还允许您为参数定义默认值,如清单 6 所示:
清单 6. 默认参数值!

greeting = (recipient = "world") ->
  "Hello #{recipient}"

console.log greeting "Andy" //prints Hello Andy
console.log greeting()      //prints Hello world

清单 7 展示了对应的 JavaScript 脚本对这种默认参数值的处理方法:
清单 7. 杂乱的 JavaScript

 var greeting;

greeting = function(recipient) {
 if (recipient == null) recipient = "world";
 return "Hello " + recipient;
};

条件

CoffeeScript 可通过引入 and、or 和 not 等关键字处理条件,如清单 8 所示:
清单 8. CoffeeScript 条件

 capitalize = (word) ->
    if word? and typeof(word) is 'string'
        word.charAt(0).toUpperCase() + word.slice 1
    else
        word

console.log capitalize "andy"   //prints Andy
console.log capitalize null     //prints null
console.log capitalize 2        //prints 2
console.log capitalize "betty"  //prints Betty

在清单 8 中,我利用了 ? 操作符来测试条件的存在与否。在尝试将一个词的首字母转为大写之前,这段脚本将确保参数 word 不是 null,同时保证它确属 string 类型。CoffeeScript 的出色之处在于允许您使用 is 来取代 ==。


函数式编程的类定义

JavaScript 并不直接支持类;它是一种面向原型的语言。对于那些仍然沉浸在面向对象编程中的人来说,这可能让人感到迷惑不解 ― 我们想要自己的类!为了满足这种要求,CoffeeScript 提供了一种 class 语法,在编译为标准 JavaScript 时,能获得函数内定义的一系列函数。

在清单 9 中,我使用 class 关键字定义了一个名为 Message 的类:
清单 9. CoffeeScript 确实支持类

class Message
    constructor: (@to, @from, @message) ->
    
    asJSON:  ->
        JSON.stringify({to: @to, from: @from, message: @message})

mess = new Message "Andy", "Joe", "Go to the party!"
console.log mess.asJSON()

在 清单 9 中,我使用 constructor 关键字定义了一个构造函数。随后,我输入了一个名称,后接一个函数,我用这种方式定义了一个方法 (asJSON)。


CoffeeScript 与 Node

CoffeeScript 脚本将编译为 JavaScript 脚本,因此 CoffeeScript 是在 Node 中进行编程的理想选择,在简化 Node 原本已经非常整洁的代码方面也是非常有帮助的。CoffeeScript 极其擅长简化 Node 的多种回调,通过一个简单的代码对比即可看出这一点。在清单 10 中,我使用纯 JavaScript 方法定义了一个简单的 Node Web 应用程序:
清单 10. 使用 JavaScript 编写的一个 Node.js web 应用程序

var express = require('express');

var app = express.createServer(express.logger());

app.put('/', function(req, res) {
  res.send(JSON.stringify({ status: "success" }));
});

var port = process.env.PORT || 3000;

app.listen(port, function() {
  console.log("Listening on " + port);
});

在 CoffeeScript 中重新编写相同的 Web 应用程序,消除 Node 回调的复杂语法,如清单 11 所示:
清单 11. CoffeeScript 简化了 Node.js

express = require 'express'

app = express.createServer express.logger()

app.put '/', (req, res) ->
  res.send JSON.stringify { status: "success" }

port = process.env.PORT or 3000

app.listen port, ->
  console.log "Listening on " + port

在 清单 11 中,我添加了一个 or 操作符,取代了 JavaScript ||。此外,我还发现,使用箭头来表示 app.listen 中的匿名函数比直接键入 function() 更容易。
CoffeeScript 就像日常语言

现在,您很可能已经认识到,CoffeeScript 倾向于使用抽象符号的日常英语表述形式。在 CoffeeScript 中,我们不是键入 !==,而是可以使用更加直观的 isnt;同样,=== 也变为了 is。

如果您对这个文件执行 coffee -c,那么就会看到 CoffeeScript 生成了与 清单 10 所示几乎完全相同的 JavaScript 脚本。CoffeeScript 中 100% 有效的 JavaScript 脚本可以配合任何 JavaScript 库一起使用。

通过 Underscore 实现函数式集合

作为 JavaScript 编程的函数式实用工具,Underscore.js 是一个能够简化 JavaScript 开发的函数库。除了其他功能之外,Underscore 还提供了一组丰富的面向集合的函数,非常适合处理特殊任务。

举例来说,假设您需要找到一个数字集合内的所有奇数,该数字集合包含从 0 到 10(不含 10)的数字。尽管您能解决这个问题,但结合使用 CoffeeScript 和 Underscore 能使您节约大量键入时间,或许还能减少一些 bug。在清单 12 中,我提供了基本算法,而 Underscore 提供了聚合函数,即本例中的 filter:
清单 12. Underscore 的 filter 函数

 _ = require 'underscore'

numbers = _.range(10)

odds = _(numbers).filter (x) ->
    x % 2 isnt 0

console.log odds

首先,由于 _(也就是 underscore)是一个有效的变量名,因此我将其设置为引用 Underscore 库。接下来,我将一个匿名函数附加到了测试奇数的 filter 函数。请注意,我使用了 CoffeeScript isnt 关键字,而非 JavaScript 的 !== 关键字。随后我使用 range 函数指定我希望排序数字 0 至 9,此外,我还为我的范围指定了一个步进计数(即按 2 计数),并从任何数字开始。

filter 函数返回一个数组,这是传递给该函数的数组经过过滤之后的版本,在本例中,返回的数组是 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]。因此运行 清单 12 中的代码将得到 [ 1, 3, 5, 7, 9 ]。

map 函数是另外一个我最常应用于 JavaScript 中的集合的函数,如清单 13 所示:
清单 13. Underscore 的 map 函数

 oneUp = _(numbers).map (x) ->
    x + 1
    
console.log oneUp

在这里,输出结果应该是 [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]。通常,Underscore 会将 numbers 范围内的各值递增 1,因此我不必手动遍历每一个整数。

如果您需要测试一个集合的多个方面,Underscore 能帮助您简化一切!只需创建一个类似于清单 14 所示的函数即可,这个函数用于测试偶数:
清单 14. Underscore 的 even 函数

 even = (x) ->
    x % 2 is 0
    
console.log _(numbers).all(even)
console.log _(numbers).any(even)

定义了 even 函数之后,即可轻松将其连接到 Underscore 函数,如 all 和 any。在本例中,all 将我的 even 函数应用到 numbers 范围中的每一个值。随后返回一个布尔值,指示是否所有 值均为偶数 (false)。类似地,如果有任何 值是偶数 (true),则 any 函数将返回布尔值 true。
利用 Underscore 完成更多任务

本文仅能简单介绍 Underscore 的部分概况。Underscore 的其他特点还包括函数绑定、JavaScript 模板编写和深度相等性的测试。(请参见 参考资料 部分。)

如果您不需要对一个值集合应用任何此类函数,而是需要执行其他一些操作,那么又该怎样做?完全没有问题!利用 Underscore 的 each 函数即可。each 函数作为一个易用的迭代器(也就是说,它能处理场景背后的循环逻辑,在每次迭代时传入指定的函数)。如果您使用过 Ruby 或者 Groovy,那么应该对这种函数感到非常熟悉。
清单 15. Underscore 的 each 函数

_.each numbers, (x) ->
    console.log(x)

在清单 15 中,each 函数获取一个集合(我的 numbers 范围)和一个需要应用于迭代数组中各值的函数。在本例中,我使用 each 将当前迭代的值输出到控制台。对于我来说,需要做的事情就像将数据保存到数据、将结果返回给用户那样简单。

结束语

CoffeeScript 给 JavaScript 编程注入了新鲜感,也简化了 JavaScript 编程,因此任何用户都能够轻松上手,尤其是熟悉 Ruby 或 Python 的用户。在本文中,我展示了 CoffeeScript 如何通过借鉴这些语言,使 JavaScript 风格的代码更易于阅读,同时还能显著加快编写过程。正如我所演示的那样,将 CoffeeScript、Node 与 Underscore 相结合,即可得到超轻量级的有趣开发堆栈 (development stack),该堆栈适用于基本函数式编程场景。经过一段时间的练习,您就可以将本文所学知识作为基础,深入研究依靠动态 Web 和移动交互的更为复杂的业务应用程序。

本文来源:http://www.bbyears.com/jiaocheng/82870.html