最近花了一些时间把CoffeeScript学习了一下, 说实话, 习惯了原生JavaScript的语法和格式, 对于Coffee还真有点不太适应: 一是在Coffee里基本上都不会去写分号和括号, 大括号啥的(对于一个有分号强迫症的人来说, 这还真得适应一段时间); 然后就是, 使用类似于Python的那种语法格式, 通过代码缩进来让编译器进行推导.

不过, 本文的主要目的当然不是不是吐槽Coffee, 而是我在看文档的过程中, 遇到了一个问题, 查阅了一些资料后, 还是解决了我心中的疑惑, 于是便拿出来分享一下.

在Coffee的官方文档中, 有下面这样一段代码(我截取了其中的一小部分, 在文档的Classes, Inheritance, and Super部分):

1
2
3
4
5
class Animal
constructor: (@name) ->
move: (meters) ->
alert @name + " moved #{meters}m."

这段Coffee所对应的JavaScript如下(我也只是截取了一小部分):

1
2
3
4
5
6
7
8
9
10
11
Animal = (function() {
function Animal(name) {
this.name = name;
}
Animal.prototype.move = function(meters) {
return alert(this.name + (" moved " + meters + "m."));
};
return Animal;
})();

那么问题来了. constructor是Animal类的构造函数, 这个没什么问题, 然后, 在Animal这个类里, 定义了一个move方法, 按理说这个方法属于Animal类. 当然, 确实属于. 但是, 奇怪的是, 为什么它会默认添加到Animal的prototype上去呢?

一开始, 我以为是constructor在作怪, 就把constructor这一块去掉了, 就像这样:

1
2
3
class Animal
move: (meters) ->
alert @name + " moved #{meters}m."

但事实证明, 我的想法是完全错误的. 这段Coffee所编译出来的JavaScript中, move方法还是在Animal.prototype上.

然后, 我在move方法下面又定义了一些变量和方法, 但无一例外, 全部都附加到了Animal.prototype上.

于是, 我上Google去查了一下, 看到了一篇比较好的文章说了这个问题: Using private methods in CoffeeScript.

那么, 如果要在Coffee中定义私有成员变量的话, 该怎么做呢?

其实很简单, 就像下面这样:

1
2
3
4
5
6
7
8
9
10
class Animal
constructor: (@name) ->
publicMethod: ->
'this is a public method'
privateMethod = ->
'this is a private method'
privateVariable = 'private'

所以, 不难看出来, 在Coffee中定义公有成员和私有成员的差别就是使用:还是=. 所以, 通过使用=, 我们就可以在Coffee中为类定义私有成员变量了.

这里,说一点关于 JavaScript 语言特性的问题。以下的内容参考自 CoffeeScript Private Class Instance Variables

以上的方法确实能够在 CoffeeScript 中实现所谓的”私有成员”。但是,需要知道的是,”私有成员”这个概念在 CoffeeScript 和 JavaScript 中本身就是不存在的。尽管看上去某个变量可能实现了”私有”,但这不是真正私有的,只是使用了 JavaScript 语言的特性( 说的更详细一点,就是闭包或者说函数作用域 )来实现了这个功能。

所以,不需要浪费时间在添加一些本身不存在的概念上面,只管好好的解决问题就好了。