为CoffeeScript类创建名称空间

时间:2012-11-26 22:35:31

标签: coffeescript

如何为使用CoffeeScript编写的类创建命名空间?

例如,我有树类 Aa Bb Cc 。我希望它们插入到globaly assesible命名空间 - MyClasses ,允许交叉它们并在jasmine-node中使用它们。

class MyClasses.Aa
 @someProp: true

class MyClasses.Bb
 @someProp2: false

class MyClasses.Cc
 @doSomeStuff: -> MyClasses.Aa.someProp = false

我知道,我可以将它们注入一个文件并进行编译,但我希望有一个类=一个文件。

我该怎么办呢? 谢谢!

编辑:我试过这种方式,但我觉得它不好,但它适用于浏览器和jasmine-node

root = exports ? this
root.MyClasses = root.MyClasses ? {}

root.MyClasses.Aa = 

  class Aa

3 个答案:

答案 0 :(得分:2)

使用RequireJS。

在一个名为“my-classes.coffee”的文件中,定义命名空间。

define [], ->
  # You need this if you want it to work in multiple environments.
  # Otherwise just use `window` to work in the browser.
  root = exports ? this

  root.MyClasses = {}

您可以在另一个名为“aa.coffee”的文件中定义您的课程。

define ['my-classes'], (MyClasses) ->

  class MyClasses.Aa
    @someProp: true

另一个档案:

define ['my-classes'], (MyClasses) ->

  class MyClasses.Bb
    @someProp2: false

现在,当您需要时,它应导出MyClasses,其中包含MyClasses.Aa

require ['my-classes', 'aa'], (MyClasses, _) ->
  console.log MyClasses.Aa.someProp

这样做的一个问题是你不能只依赖require语句中的“my-classes”。如果您这样做,MyClasses.Aa将是未定义的。但是你也不能只依赖“aa”,因为“aa”除了通过添加到MyClasses之外不会导出任何东西。在上面的代码段中,MyClasses.Bb未定义,因为我没有明确依赖它。这就是为什么许多人要么使用一个巨大的文件,要么复制重新导出命名空间的样板。

如果有人知道如何解决这个问题,请告诉我。

我个人认为RequireJS使用起来很复杂,并且有许多不同的方法来设置它。我使用jasmine的一种方法是使用cake任务将我的CoffeeScript预编译为JavaScript,然后使用这样的spec文件。

requirejs = require('requirejs')
# Set the baseURL to your compiled JS dir.
requirejs.config { baseUrl: __dirname + '/../lib' }

requirejs ['my-classes', 'aa'], (MyClasses, _) ->

  describe "someProp", ->
    it "should be true", ->
      expect(MyClasses.Aa.someProp).toEqual true

这可能不是最好的方法,但我能够使用它在浏览器,节点服务器和jasmine-node测试中运行模块。我也看到有些人使用custom runners来避免spec文件中的样板文件。

如果您不想使用RequireJS,可能会发现this question有帮助。它的工作原理是使用CoffeeScript FAQs上定义的namespace函数。

答案 1 :(得分:1)

Coffeescript wiki中有一个建议的解决方案:

来自https://github.com/jashkenas/coffee-script/wiki/FAQ

# Code:
#
namespace = (target, name, block) ->
  [target, name, block] = [(if typeof exports isnt 'undefined' then exports else window), arguments...] if arguments.length < 3
  top    = target
  target = target[item] or= {} for item in name.split '.'
  block target, top

# Usage:
#
namespace 'Hello.World', (exports) ->
  # `exports` is where you attach namespace members
  exports.hi = -> console.log 'Hi World!'

namespace 'Say.Hello', (exports, top) ->
  # `top` is a reference to the main namespace
  exports.fn = -> top.Hello.World.hi()

Say.Hello.fn()  # prints 'Hi World!'

答案 2 :(得分:0)

您可以使用-b标记编译CoffeeScript文件,从而删除安全包装。或者用你已经拥有的东西将你的类暴露到全局范围

a.coffee

root = exports ? window
MyClasses = root.MyClasses = root.MyClasses ? {}

class MyClasses.Aa
  @someProp: true

b.coffee

# same header as a.coffee
class MyClasses.Bb
  @someProp2: false

c.coffee

# same header as a.coffee
class MyClasses.Cc
  @doSomeStuffWith: (someClass)-> 
    # pass a class to the method instead of just 
    # modifying another class within the same scope
    someClass.someProp = false

重复其他课程。在第一行中,我发现windowthis更明确,第二行也允许您在没有长命名空间序列的情况下将类附加到MyClasses

在浏览器中,您的类将位于全局对象MyClasses

在node.js中你会像这样使用它们(这是一个有点冗长的恕我直言):

var Aa = require("./a").MyClasses.Aa,
    Bb = require("./b").MyClasses.Bb,
    Cc = require("./c").MyClasses.Cc;

console.log(Aa.someProp)  // true
console.log(Bb.someProp2) // false

Cc.doSomeStuffWith(Aa)

console.log(Aa.someProp) // false

pd:我没有用node-jasmine检查它。