CoffeeScript'teki özel üyeler?


84

CoffeeScript'te özel, statik olmayan üyelerin nasıl oluşturulacağını bilen var mı? Şu anda, sınıfın dışında kullanılmaması gerektiğini açıklığa kavuşturmak için alt çizgiyle başlayan bir genel değişken kullanan bunu yapıyorum:

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

  getName: -> @_name

Değişkeni sınıfa koymak onu statik bir üye yapar, ancak onu nasıl statik olmayan yapabilirim? "Süslenmeden" mümkün mü?

Yanıtlar:


20

"Süslenmeden" mümkün mü?

Söylemesi üzücü, süslü olmalısın .

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

Unutmayın, "Bu sadece JavaScript."


1
... ve bu yüzden bunu JS'de yaptığınız gibi yapmalısınız. Tüm o şekerin arkasına gizlendiğinde unutmak kolay, teşekkürler!
thejh

4
Bu gerçekten özel mi? Buna sınıfın dışından da erişebilirsiniz. a = Thing ('a') sonra a.getName () değeri döndürür ve a.getName = -> 'b' onu ayarlar.
Amir

4
@Amir: nameyalnızca yapıcı kapağının içinden görülebilir. Şu öze
thejh

13
Ayrıca @getName = -> name, getNameişlevin olası herhangi bir kalıtımını bozuyor gibi göründüğünü de belirtmek gerekir .
Kendall Hopkins

12
Bu cevap yanlıştır: Burada geneldir getNameve nameyalnızca yapıcı işlevinden erişilebilir, dolayısıyla nesneye gerçekten "özel" değildir.
tothemario

203

sınıflar sadece işlevlerdir, bu nedenle kapsamları oluştururlar. bu kapsamda tanımlanan her şey dışarıdan görünmez.

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 bunu şu şekilde derler:

(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);

9
Bu özel değişkenlerin alt sınıflar için mevcut olmadığı unutulmamalıdır .
Ceasar Bautista

45
İşlevin örneği foo.call(this)olabilmeleri için 'özel' yöntemlerin benzer şekilde çağrılması gerektiğine de dikkat edilmelidir this. Bu nedenle JavaScript'te klasik mirası taklit etmeye çalışmak zorlaşıyor.
Jon Wingfield

3
Bir başka dezavantajı ise, birim testi için "özel" yöntemlere erişiminizin olmayacak olmasıdır ..
nuc

16
@nuc private yöntemleri, onları çağıran genel yöntemlerle test edilen uygulama ayrıntılarıdır, yani özel yöntemlerin birim testi yapılmaması gerekir. Özel bir yöntem birim test edilebilir gibi görünüyorsa, belki de halka açık bir yöntem olmalıdır. İyi bir açıklama için bu
gönderiye bakın

2
Ayrıca, "özel" değişkenlerinizi "genel" işlevlerde kullanıldığı yerde yukarıda tanımlamanız gerekeceği unutulmamalıdır. Aksi takdirde, CoffeeScript'in kafası karışacak ve varonları gölgeleyecek yeni dahili bildirimler oluşturacaktır.
Andrew Miner

11

Daha da meraklısı bir şey göstermek istiyorum

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

Şimdi yapabilirsin

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

2

Vitaly'nin cevabıyla ilgili bir sorun var ve bu, kapsam için benzersiz olmasını istediğiniz değişkenleri tanımlayamamanızdır , bu şekilde özel bir ad oluşturup sonra değiştirirseniz, ad değeri sınıfın her bir örneği için değişir, bu sorunu çözmenin bir yolu var

# 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

Kullanmadığınız sürece names dizisine dışarıdan erişmek imkansız değildir. getNames

Bunu test et

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']

Derlenmiş 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;
};

Bu uygulamayı seviyorum. Herhangi bir sakınca var mı?
Erik5388

2

Burada, buradaki diğer cevapların birçoğuna ek olarak https://stackoverflow.com/a/7579956/1484513'ten yararlanan bir çözüm var . Özel örnek (statik olmayan) değişkenleri bir özel sınıf (statik) dizisinde depolar ve bu dizinin hangi öğesinin her örneğe ait verileri içerdiğini bilmek için bir nesne kimliği kullanır.

# 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

2

İşte ben ayarı hakkında buldum iyi makale public static members, private static members, public and private membersve bazı diğer ilgili şeyler. Çok daha ayrıntıları ve kapakları jsvs coffeekarşılaştırma. Ve tarihsel nedenlerden ötürü, işte ondan en iyi kod örneği:

# 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

İşte Coffeescript'te özel, statik olmayan üyeleri nasıl ilan edebileceğiniz
Tam referans için https://github.com/vhmh2005/jsClass adresine göz atabilirsiniz.

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

1

Kahve betiklerinde "sınıf", prototip bazlı bir sonuca götürür. Dolayısıyla, özel bir değişken kullansanız bile, örnekler arasında paylaşılır. Bunu yapabilirsiniz:

EventEmitter = ->
  privateName = ""

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

.. sebep olur

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

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

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

Ancak özel üyeleri genel işlevlerin önüne koymaya dikkat edin, çünkü kahve yazısı genel işlevleri nesne olarak döndürür. Derlenmiş Javascript'e bakın:

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

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

0

Kahve betiği JavaScript'e derlendiğinden, özel değişkenlere sahip olmanın tek yolu kapanışlardır.

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

Bu, aşağıdaki JavaScript aracılığıyla derlenecektir:

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

Elbette bu, kapatmalar aracılığıyla sahip olabileceğiniz diğer tüm özel değişkenlerle aynı sınırlamalara sahiptir, örneğin, yeni eklenen yöntemler aynı kapsamda tanımlanmadıkları için bunlara erişemez.


9
Bu bir tür statik üye. e = new Animal(5);f = new Animal(1);e.test()uyarı bir, beş istiyorum.
thejh

@thejh Oh, pardon, şimdi hatayı görüyorum, sanırım dün bu şeyleri düşünmek için çok geçti.
Ivo Wetzel

@thejh Bu bana oldu, cevabımda bu sorunu çözmeye çalıştım.
iConnor

0

Bunu CoffeeScript sınıflarıyla kolayca yapamazsınız, çünkü sınıflar oluşturmak için Javascript yapıcı modelini kullanırlar.

Ancak şöyle bir şey söyleyebilirsin:

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}"

Ancak, CoffeeScript sınıflarının büyüklüğünü kaybedersiniz, çünkü bu şekilde oluşturulmuş bir sınıftan ext () kullanmaktan başka bir yolla miras alamazsınız. instanceof çalışmayı durdurur ve bu şekilde oluşturulan nesneler biraz daha fazla bellek tüketir. Ayrıca, artık yeni ve süper anahtar kelimeleri kullanmamalısınız .

Buradaki önemli nokta, bir sınıfın her örneği oluşturulduğunda kapanışların oluşturulması gerektiğidir. Saf CoffeeScript sınıflarındaki üye kapanışları yalnızca bir kez oluşturulur - yani, sınıf çalışma zamanı "türü" oluşturulduğunda.


-3

Yalnızca özel üyelerin genel üyelerden ayrı olmasını istiyorsanız, bunu $ değişkene sarın

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

ve kullan @$.requirements

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.