2011-01-13 94 views
85

有人知道如何在CoffeeScript中創建私有的非靜態成員嗎​​?目前,我正在做這個,剛剛使用了公共變量開始用下劃線澄清,它不應該之類的外部使用:CoffeeScript中的私人成員?

class Thing extends EventEmitter 
    constructor: (@_name) -> 

    getName: -> @_name 

把變量的類使靜態成員,但我怎樣才能使它非靜態?它甚至可能沒有變得「看中」?

回答

20

它甚至有可能沒有得到「神奇」?

很遺憾地說,你必須是看中

class Thing extends EventEmitter 
    constructor: (name) -> 
    @getName = -> name 

記住,「這只是JavaScript的。」

+1

...所以你必須這樣做,因爲你會在JS中這樣做。當它隱藏在所有糖後面時,很容易忘記它,謝謝! – thejh 2011-01-14 18:01:02

0

由於咖啡腳本編譯爲JavaScript,所以您可以擁有私有變量的唯一方法是通過閉包。

class Animal 
    foo = 2 # declare it inside the class so all prototypes share it through closure 
    constructor: (value) -> 
     foo = value 

    test: (meters) -> 
    alert foo 

e = new Animal(5); 
e.test() # 5 

這將編譯向下通過下面的JavaScript:

var Animal, e; 
Animal = (function() { 
    var foo; // closured by test and the constructor 
    foo = 2; 
    function Animal(value) { 
    foo = value; 
    } 
    Animal.prototype.test = function(meters) { 
    return alert(foo); 
    }; 
    return Animal; 
})(); 

e = new Animal(5); 
e.test(); // 5 

當然這所有相同的限制,因爲所有其他私有變量,你可以通過使用封閉的,例如,新添加的方法無法訪問它們,因爲它們沒有在相同的範圍內定義。

+9

這是一種靜態成員。 `e = new Animal(5); f = new Animal(1); e.test()`提醒一個,我想要五個。 – thejh 2011-01-13 22:21:37

+0

@thejh哦,對不起,現在我看到了這個錯誤,猜想昨天想想這件事太晚了。 – 2011-01-14 07:22:26

+0

@thejh發生在我身上,我試圖在我的答案中解決這個問題。 – iConnor 2014-02-24 03:09:55

204

類只是功能,所以他們創建範圍。在此範圍內定義的所有內容都不會從外部看到。

class Foo 
    # this will be our private method. it is invisible 
    # outside of the current scope 
    foo = -> "foo" 

    # this will be our public method. 
    # note that it is defined with ':' and not '=' 
    # '=' creates a *local* variable 
    # : adds a property to the class prototype 
    bar: -> foo() 

c = new Foo 

# this will return "foo" 
c.bar() 

# this will crash 
c.foo 

的CoffeeScript編譯此爲以下:

(function() { 
    var Foo, c; 

    Foo = (function() { 
    var foo; 

    function Foo() {} 

    foo = function() { 
     return "foo"; 
    }; 

    Foo.prototype.bar = function() { 
     return foo(); 
    }; 

    return Foo; 

    })(); 

    c = new Foo; 

    c.bar(); 

    c.foo(); 

}).call(this); 
0

你不能用CoffeeScript的類容易做,因爲他們使用的JavaScript構造模式創建的類。

但是,你可以說這樣的事情:

callMe = (f) -> f() 
extend = (a, b) -> a[m] = b[m] for m of b; a 

class superclass 
    constructor: (@extra) -> 
    method: (x) -> alert "hello world! #{x}#{@extra}" 

subclass = (args...) -> extend (new superclass args...), callMe -> 
    privateVar = 1 

    getter: -> privateVar 
    setter: (newVal) -> privateVar = newVal 
    method2: (x) -> @method "#{x} foo and " 

instance = subclass 'bar' 
instance.setter 123 
instance2 = subclass 'baz' 
instance2.setter 432 

instance.method2 "#{instance.getter()} <-> #{instance2.getter()} ! also, " 
alert "but: #{instance.privateVar} <-> #{instance2.privateVar}" 

但是你使用擴展失去的CoffeeScript類的偉大,因爲你不能從任何其他方式創建的方法比類繼承( )。 instanceof將停止工作,並且以此方式創建的對象消耗更多內存。此外,您不能再使用新的超級關鍵字。

的一點是,該關閉必須創建每次類實例化時間。純CoffeeScript類中的成員閉包只創建一次 - 也就是說,構建類運行時「類型」時。

10

我想展示一些東西更炫

class Thing extends EventEmitter 
    constructor: (nm) -> 
    _name = nm 
    Object.defineProperty @, 'name', 
     get: -> 
     _name 
     set: (val) -> 
     _name = val 
     enumerable: true 
     configurable: true 

現在你可以做

t = new Thing('Dropin') 
# members can be accessed like properties with the protection from getter/setter functions! 
t.name = 'Dragout' 
console.log t.name 
# no way to access the private member 
console.log t._name 
-3

如果你想從公共只有獨立的私人承包商,客人,只是把它包在$變量

$: 
     requirements: 
       {} 
     body: null 
     definitions: null 

and use @$.requirements

2

維塔利的答案有一個問題,那就是你不能定義你想成爲範圍的唯一的變量,如果你這樣做了一個私人名稱然後改變了它,名稱值會改變爲每一個單一的類的實例,所以有一個辦法可以解決這個問題

# create a function that will pretend to be our class 
MyClass = -> 

    # this has created a new scope 
    # define our private varibles 
    names = ['joe', 'jerry'] 

    # the names array will be different for every single instance of the class 
    # so that solves our problem 

    # define our REAL class 
    class InnerMyClass 

     # test function 
     getNames: -> 
      return names; 

    # return new instance of our class 
    new InnerMyClass 

這不是不可能從外部訪問的名字排列,除非您使用getNames

測試了這一點

test = new MyClass; 

tempNames = test.getNames() 

tempNames # is ['joe', 'jerry'] 

# add a new value 
tempNames.push 'john' 

# now get the names again 
newNames = test.getNames(); 

# the value of newNames is now 
['joe', 'jerry', 'john'] 

# now to check a new instance has a new clean names array 
newInstance = new MyClass 
newInstance.getNames() # === ['joe', 'jerry'] 


# test should not be affected 
test.getNames() # === ['joe', 'jerry', 'john'] 

編譯的JavaScript

var MyClass; 

MyClass = function() { 
    var names; 
    names = ['joe', 'jerry']; 
    MyClass = (function() { 

    MyClass.name = 'MyClass'; 

    function MyClass() {} 

    MyClass.prototype.getNames = function() { 
     return names; 
    }; 

    return MyClass; 

    })(); 
    return new MyClass; 
}; 
1

這裏是如何在CoffeeScript中
聲明私有,非靜態成員對於完全參考,你可以看看https://github.com/vhmh2005/jsClass

class Class 

    # private members 
    # note: '=' is used to define private members 
    # naming convention for private members is _camelCase 

    _privateProperty = 0 

    _privateMethod = (value) ->   
    _privateProperty = value 
    return 

    # example of _privateProperty set up in class constructor 
    constructor: (privateProperty, @publicProperty) -> 
    _privateProperty = privateProperty 
2

下面是一個解決方案這裏引用了其他幾個答案,加上https://stackoverflow.com/a/7579956/1484513。它將私有實例(非靜態)變量存儲在私有類(靜態)數組中,並使用對象ID來知道該數組的哪個元素包含屬於每個實例的數據。

# Add IDs to classes. 
(-> 
    i = 1 
    Object.defineProperty Object.prototype, "__id", { writable:true } 
    Object.defineProperty Object.prototype, "_id", { get: -> @__id ?= i++ } 
)() 

class MyClass 
    # Private attribute storage. 
    __ = [] 

    # Private class (static) variables. 
    _a = null 
    _b = null 

    # Public instance attributes. 
    c: null 

    # Private functions. 
    _getA = -> a 

    # Public methods. 
    getB: -> _b 
    getD: -> __[@._id].d 

    constructor: (a,b,@c,d) -> 
    _a = a 
    _b = b 

    # Private instance attributes. 
    __[@._id] = {d:d} 

# Test 

test1 = new MyClass 's', 't', 'u', 'v' 
console.log 'test1', test1.getB(), test1.c, test1.getD() # test1 t u v 

test2 = new MyClass 'W', 'X', 'Y', 'Z' 
console.log 'test2', test2.getB(), test2.c, test2.getD() # test2 X Y Z 

console.log 'test1', test1.getB(), test1.c, test1.getD() # test1 X u v 

console.log test1.a   # undefined 
console.log test1._a  # undefined 

# Test sub-classes. 

class AnotherClass extends MyClass 

test1 = new AnotherClass 's', 't', 'u', 'v' 
console.log 'test1', test1.getB(), test1.c, test1.getD() # test1 t u v 

test2 = new AnotherClass 'W', 'X', 'Y', 'Z' 
console.log 'test2', test2.getB(), test2.c, test2.getD() # test2 X Y Z 

console.log 'test1', test1.getB(), test1.c, test1.getD() # test1 X u v 

console.log test1.a   # undefined 
console.log test1._a  # undefined 
console.log test1.getA() # fatal error 
1

Here's我發現有關設置public static membersprivate static memberspublic and private members,以及其他一些相關的東西的最好的文章。它涵蓋了很多細節,jscoffee比較。而對於歷史原因,這裏是最好的代碼示例從它:

# CoffeeScript 

class Square 

    # private static variable 
    counter = 0 

    # private static method 
    countInstance = -> 
     counter++; return 

    # public static method 
    @instanceCount = -> 
     counter 

    constructor: (side) -> 

     countInstance() 

     # side is already a private variable, 
     # we define a private variable `self` to avoid evil `this` 

     self = this 

     # private method 
     logChange = -> 
      console.log "Side is set to #{side}" 

     # public methods 
     self.setSide = (v) -> 
      side = v 
      logChange() 

     self.area = -> 
      side * side 

s1 = new Square(2) 
console.log s1.area() # output 4 

s2 = new Square(3) 
console.log s2.area() # output 9 

s2.setSide 4   # output Side is set to 4 
console.log s2.area() # output 16 

console.log Square.instanceCount() # output 2 
1

「類」咖啡腳本導致了基於原型的結果。因此,即使您使用私有變量,它也會在實例之間共享。你可以這樣做:

EventEmitter = -> 
    privateName = "" 

    setName: (name) -> privateName = name 
    getName: -> privateName 

..導致

emitter1 = new EventEmitter() 
emitter1.setName 'Name1' 

emitter2 = new EventEmitter() 
emitter2.setName 'Name2' 

console.log emitter1.getName() # 'Name1' 
console.log emitter2.getName() # 'Name2' 

但要小心,把公共職能前的私有成員,因爲咖啡腳本返回的公共職能爲目標。看看編譯的Javascript:

EventEmitter = function() { 
    var privateName = ""; 

    return { 
    setName: function(name) { 
     return privateName = name; 
    }, 
    getName: function() { 
     return privateName; 
    } 
    }; 
};