世界上的很多天才都在为构建更好的JavaScript而努力。已经有了很多尝试,其中最有前途的,无非就是CoffeeScript和TypeScript了。面对CoffeeScript,我有一见如故的感觉;而TypeScript也激发了我极大的兴趣。CoffeeScript和TypeScript一样,都是编译为JavaScript的语言,它们都增强了JavaScript的表达能力。这篇文章是讲CoffeeScript的,TypeScript将放在下一篇再讲。

所谓编译为JavaScript,是指CoffeeScript和TypeScript没有实现自己的运行时,它们都是编译为等价的JavaScript代码,然后放在JavaScript的解释器上运行。

CoffeeScript

简洁性

CoffeeScript给人最大的印象就是其简洁的表达。下面代码是我从CoffeeScript中文摘抄下来的:

# 赋值:
number   = 42
opposite = true# 条件:
number = -42 if opposite# 函数:
square = (x) -> x * x# 数组:
list = [1, 2, 3, 4, 5]# 对象:
math =root:   Math.sqrtsquare: squarecube:   (x) -> x * square x# Splats:
race = (winner, runners...) ->print winner, runners# 存在性:
alert "I knew it!" if elvis?# 数组 推导(comprehensions):
cubes = (math.cube num for num in list)

上面的代码会编译为等价的JavaScript代码:

var cubes, list, math, num, number, opposite, race, square,__slice = [].slice;number = 42;opposite = true;if (opposite) {number = -42;
}square = function(x) {return x * x;
};list = [1, 2, 3, 4, 5];math = {root: Math.sqrt,square: square,cube: function(x) {return x * square(x);}
};race = function() {var runners, winner;winner = arguments[0], runners = 2 <= arguments.length ? __slice.call(arguments, 1) : [];return print(winner, runners);
};if (typeof elvis !== "undefined" && elvis !== null) {alert("I knew it!");
}cubes = (function() {var _i, _len, _results;_results = [];for (_i = 0, _len = list.length; _i < _len; _i++) {num = list[_i];_results.push(math.cube(num));}return _results;
})();run: cubes

CoffeeScript力求简洁。其简洁性首先表现在对一些仅用于语法控制的符号进行了去除。这其中包括:

  • 取消分号

  • 取消var声明

  • 取消大括号包围内层代码,使用缩进取代

  • 函数调用在没有歧义的情况下可以省略括号

var声明涉及到复杂又很鸡肋的JavaScript变量作用域机制。这部分内容先放着不讲。CoffeeScript通过完全取消var声明机制而使得问题得到简化。总之,在CoffeeScript世界里,变量不用事先声明,直接用就是了。而且这种用法基本没有什么危险。

缩进在CoffeeScript中不仅仅在于美化代码,其代表了代码层次的组织,是有特别的含义的。简单地说就是,不适用大括号包围内层代码,而是内层代码要缩进。不同的缩进代表了不同的代码层次。形式和内容是一致的。

缩进的例子:

#if缩进
if true'true'
else'false'#while缩进
while true'true'#函数缩进
(n) ->n * n#对象字面量缩进
kids =brother:name: "Max"age:  11sister:name: "Ida"age:  9 

在不引起歧义的情况下,CoffeeScript的函数调用可以省略括号。例如console.log(object)可以简化为console.log object。所谓引起歧义的一个例子就是无参数的情况下,console.log就不知道是取出函数型属性log还是调用函数log了。

CoffeeScript的函数表达式也做了极致的精简精简。一个单行函数的定义可以这样:

square = (x) -> x * x

而多行函数也是通过缩进来组织的。一个空的函数最为简洁,是这样:->

函数的这种简洁表达使得传递回调函数非常便利。一个数组的map可能像这样就足够了:

list = [1, 2, 3]
list.map (e) -> e+1

而等效的JavaScript代码就不能这么马虎:

list = [1, 2, 3];
list.map(function(e) { return e + 1; });

增强的表达

CoffeeScript提供了JavaScript所没有的一些强大的表达语法,这也是被称为语法糖的东西。在我印象中,这种增强性是很多的,我举出两个有代表性的例子:

  • 字符串插值法

  • 列表解析

其中字符串插值法是对现有字符串能力的一种扩充和语法上的简化;而列表解析就要涉及到观念上的改变了。前者是一种改良,后者则是一种变革。

字符串插值法

在CoffeeScript的字符串里,可以用#{…}嵌入一个表达式。例如:

"#{ 22 / 7 } is a decent approximation of π"

等价于:

"" + (22 / 7) + " is a decent approximation of π";

插值在这里起到占位的作用,使得动态内容的字符串更容易构建。我想人人都能接受这样的表达。

列表解析

列表解析是CoffeeScript的世界里的重要一员。它改变了循环的思路。CoffeeScript没有提供像JavaScript那样的for循环结构,而是统统转化为列表解析。一个常规的JavaScript for循环,像下面这样:

food_list = ['toast', 'cheese', 'wine'];
for (i = 0, len = food_list.length; i < len; i++) {food = food_list[i];eat(food);
}

用CoffeeScript实现就是:

food_list = ['toast', 'cheese', 'wine']
eat food for food in food_list #做个小补充,for循环的单条语句的写法

单单是上面的例子不足以显示列表解析的强大(却看到它的简洁了)。在继续这个话题之前,我觉得我有必要补充一下另一个涉及到CoffeeScript理念的东西了:一切皆是表达式

在CoffeeScript世界里,一切语句都是表达式语句,都会返回一个值。函数调用默认会返回最后一条语句的值。if条件结构也会返回值,其返回的是执行的最后一条语句的值。循环结构有些不同,其会将每次循环的结果都保存在一个数组里,作为此循环结构的值。例如下面代码的list结果就是[5, 4, 3, 2, 1]

num = 6
list = while num -= 1num

回到列表解析的主题。与while一样,for结构也是一种循环的表达,其结果也是一个数组。回到先前的例子,下面的小代码的list结果就是['t', 'c', 'w']。

food_list = ['toast', 'cheese', 'wine']
list = (food[0] for food in food_list)

我们已经看到for循环的each形式

eat food for food in food_list

以及它的map形式

(food[0] for food in food_list)

下面给出它的filter形式

(food for food in food_list when food is 'wine')

列表解析的特色的地方在于它改变了我们组织循环的方式和解析数组的模式。这是一种声明式的编程方法,告诉程序你想要什么而不去关心构建的过程。

类的支持

类是CoffeeScript对JavaScript的一个很重要的补充。JavaScript的原型功能很强大,写法上又恨别扭。正确地设置原型链以实现继承关系也是个很大的挑战。CoffeeScript从语法上直接支持类的定义,自然且隐藏细节。

class Animalconstructor: (@name) ->move: (meters) ->alert @name + " moved #{meters}m."class Snake extends Animalmove: ->alert "Slithering..."super 5class Horse extends Animalmove: ->alert "Galloping..."super 45sam = new Snake "Sammy the Python"
tom = new Horse "Tommy the Palomino"sam.move()
tom.move()

从实现上来说,CoffeeScript的类与JavaScript的构造函数和原型链那一套并无二致。所以,理解原型机制也是理解CoffeeScript类的基础。

关于JavaScript的糟粕

CoffeeScript的另一个目标是从语法层面上直接消除JavaScript的被人诟病的一些糟粕部分。前面已经说过关于分号的部分。关于var声明的部分。分号的机制暂且不去例会,总之CoffeeScript不用再去写分号了。

在JavaScript当中,最为人诟病的糟粕部分有两处,因为它们使用的情况最多而且容易出错。

  1. 全局变量

  2. 相等比较

全局变量

JavaScript的作用域规则很复杂,涉及到var声明机制和变量提升。在JavaScript里,构造一个全局变量是很容易的,有三种方式:

  1. 在全局的环境里用var声明

    var name = 'name';
  2. 在函数内用省略var的方式定义

    function foo() {name = 'name';
    }
  3. 绑定到window的属性

    window.name = 'name';

其中第1种和第2种方式是最常见的错误用法。首先不推荐直接在全局环境中编码,而是应该用一个匿名函数包裹起来,将程序的作用域限制在这个匿名函数中。第二种用法完完全全就是忘记了var声明。而我在实际的JavaScript编码中,忘记var声明是常有的事(就像经常忘记行末补上分号一样)。

而在CoffeeScript里面就完全没有这种担心了。首先,编译后的JavaScript代码不会暴露在全局环境里,所有的代码都是自动包裹在一个匿名函数(function(){ ... })();内。然后,所有的变量都会自动加上var声明。这就使得不小心污染全局的情况很难发生,除非使用赋值到window上。

相等比较

我们都知道JavaScript有两种比较运算符:=====。我们也知道==在使用的过程中会很坑,所以平时都宁愿多打一个字符而使用===。CoffeeScript的只有一种比较运算符==,而它会编译成JavaScript的===,从而很好地避过了这道坑。

是否该使用CoffeeScript

CoffeeScript简化和增强了JavaScript的表达能力,尽可能地从语法层面上就能避免JavaScript的一些坑。用它写代码,会让人有更清晰舒适的感觉,而且不容易犯错。CoffeeScript的初衷就是提供更好的JavaScript

然而,CoffeeScript与JavaScript是不兼容的。它既不是JavaScript的子集,也不是超集,而是与JavaScript有着显然不同思路的一种语言。用CoffeeScript编程就必然要转换观念,尽管这种观念更好更自然,但却是有些固步自封的人望而却步的主要原因了。

CoffeeScript并不是适合每一个人的。有些人对于用缩进组织代码层次完全不能接受,也不能接受用箭头函数表达法。对于他们来说,去掉function关键字和大括号的组织怎么看都怎么地不顺眼。

列表解析很强大,却也显得过于简洁了。对于习惯了构造冗杂JavaScript程序的人们来说,并不习惯这种表达方式。

总之,是不可强求别人去学习使用CoffeeScript。JavaScript已经足够强大,只要足够小心,完全可以使用JavaScript很好地完成工作。对于那些想要尝试CoffeeScript,我们也要给予鼓励的态度,他们是求新求变的勇士。CoffeeScript真的值得一试,而且它真的很小巧,完全掌握它不是件困难的事。

对于在团队推行CoffeeScript,我本人更是持有保守的看法。如果团队从一开始就使用CoffeeScript还好。如果是要从CoffeeScript转为JavaScript,就要谨慎行之。一种可行的方式是先尝试在一个小项目中使用CoffeeScrip,看看效果如何。

对于个人来说,就没有什么限制了。如果真的喜欢,就去尝试吧。你可以使用CoffeeScript写脚本,构建自己的网站,做一些小玩意。

转载于:https://www.cnblogs.com/starstone/p/4915479.html

(六)我的JavaScript系列:更好的JavaScript之CoffeeScript相关推荐

  1. 重学JavaScript系列——(六)集合引用类型

    重学JavaScript系列--(六)集合引用类型 博主以扎实JavaScript基础为目的,以<JavaScript高级程序设计(第四版)>为核心参考资料,以一个"复习者&qu ...

  2. javascript系列之DOM(三)---事件

    javascript系列之DOM(三)---事件 原文:javascript系列之DOM(三)---事件 事件是javascript跳动的心脏,是DOM所有成分结合的万金油.当我们在WEB 上进行某些 ...

  3. 深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点

    才华横溢的Stoyan Stefanov,在他写的由O'Reilly初版的新书<JavaScript Patterns>(JavaScript模式)中,我想要是为我们的读者贡献其摘要,那会 ...

  4. 深入理解JavaScript系列:《你真懂JavaScript吗?》答案详解

    介绍 昨天发的<大叔手记(19):你真懂JavaScript吗?>里面的5个题目,有很多回答,发现强人还是很多的,很多人都全部答对了. 今天我们来对这5个题目详细分析一下,希望对大家有所帮 ...

  5. 深入理解JavaScript系列:闭包(Closures)

    介绍 本章我们将介绍在JavaScript里大家经常来讨论的话题 -- 闭包(closure).闭包其实大家都已经谈烂了.尽管如此,这里还是要试着从理论角度来讨论下闭包,看看ECMAScript中的闭 ...

  6. 深入理解JavaScript系列:This? Yes,this!

    介绍 在这篇文章里,我们将讨论跟执行上下文直接相关的更多细节.讨论的主题就是this关键字.实践证明,这个主题很难,在不同执行上下文中this的确定经常会发生问题. 许多程序员习惯的认为,在程序语言中 ...

  7. 深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点(转)

    才华横溢的Stoyan Stefanov,在他写的由O'Reilly初版的新书<JavaScript Patterns>(JavaScript模式)中,我想要是为我们的读者贡献其摘要,那会 ...

  8. 【JavaScript系列】JS基础语法_从变量到数组一网打尽_有教程有实战

    [JavaScript系列]基础语法_从变量到数组一网打尽_有教程有实战 JavaScript基础语法目录 基础语法 延迟和异步脚本 变量 数据类型 数据类型简介 简单数据类型与复杂数据类型 栈和堆 ...

  9. 漫谈程序员(十六)健康程序猿系列之健康体魄

    #漫谈程序员(十六)健康程序猿系列之健康体魄   在外行看来,谈到程序猿,大家的第一印象就是技术人员.内行其实都明白,程序猿光鲜的另一面是时时刻刻遭受到健康的威胁.可能由于平时工作忙,没有时间运动,那 ...

  10. 计算机技能大赛比赛新闻稿,第六届技能竞赛系列报道——PS绘制青春

    原标题:第六届技能竞赛系列报道--PS绘制青春 为推动我校学生计算机平面设计水平的提高,培养学生的创新精神和实践能力,展现当代中职生科技文化的风采,活跃计算机专业技能学习氛围,鼓励优秀学生的涌现.我校 ...

最新文章

  1. 给GridView增加求和行
  2. 2021年春季学期-信号与系统-第八次作业参考答案-第七小题
  3. Vista中使用率最高的11条命令
  4. DAVINCI DM365-368中 linux-2.6.32的移植
  5. 开源PHP多应用授权系统源码
  6. vue实现一个简单的购物车功能
  7. brctl tunctl 虚拟网卡 桥接
  8. 马尔科夫区制转换matlab,马尔科夫区制转移混频向量自回归(MS-MF-VAR)模型及其Gauss实现...
  9. 如何实现高性能的在线 PDF 预览
  10. Windows11 安装 WSA 简单上手一试
  11. 冯诺依曼结构和哈佛结构
  12. 伊甸园日历游戏 c语言,HDU2149-Good Luck in CET-4 Everybody!(博弈,打表找规律)
  13. Android TV H5 电视应用
  14. 布道微服务_03服务的发布和引用
  15. SWD模式下无法识别到芯片
  16. Unity SKFramework框架(二十五)、RSA算法加密、签名工具 RSA Crypto
  17. 2020年之HBuilderX manifest.json配置最全详解
  18. 单代号网络图计算例题_电气图纸制图规范及电气图纸的识读方法、项目代号意义...
  19. 最小二乘法在飞思卡尔智能车路径搜索中的应用
  20. backtrader进行期货回测要注意的问题:保证金等设置,拼接滚动合约

热门文章

  1. OLED屏幕应用实验
  2. [CVPR2021]NeRV: Neural Reflectance and Visibility Fields for Relighting and View Synthesis
  3. 《Linux内核设计与实现》读书笔记(八)- 中断下半部的处理
  4. uva 12307(点集的外接矩形)
  5. letsencrypt证书-管理工具certbot
  6. [node]request+watch开发自测的懒人神器
  7. 嗑药简史:咖啡上瘾,喝还是不喝?
  8. 使用 Mono.Cecil 辅助 Unity3D 手游进行性能测试
  9. python-shixian考拉兹猜想
  10. 【生信分析】基于TCGA肿瘤数据进行基因共表达网络分析