Quelqu'un sait-il comment créer des membres privés non statiques dans CoffeeScript? Actuellement, je fais cela, qui utilise simplement une variable publique commençant par un trait de soulignement pour clarifier qu'elle ne devrait pas être utilisée en dehors de la classe:
class Thing extends EventEmitter
constructor: (@_name) ->
getName: -> @_name
Mettre la variable dans la classe en fait un membre statique, mais comment puis-je la rendre non statique? Est-ce même possible sans devenir "fantaisiste"?
Est-ce même possible sans devenir "fantaisiste"?
Triste à dire, il faudrait être fantaisie.
class Thing extends EventEmitter
constructor: (name) ->
@getName = -> name
Rappelez-vous, "C'est juste du JavaScript."
les classes ne sont que des fonctions afin de créer des étendues. tout ce qui est défini à l'intérieur de cette portée ne sera pas visible de l'extérieur.
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 le compile comme suit:
(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);
Je voudrais montrer quelque chose d'encore plus chic
class Thing extends EventEmitter
constructor: ( nm) ->
_name = nm
Object.defineProperty @, 'name',
get: ->
_name
set: (val) ->
_name = val
enumerable: true
configurable: true
Maintenant vous pouvez faire
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
Voici le meilleur article que j'ai trouvé sur la configuration de public static members
, private static members
, public and private members
, et quelques autres choses liées. Il couvre beaucoup de détails et js
contre coffee
comparaison. Et pour les raisons historiques , voici le meilleur exemple de code:
# 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
Il y a un problème avec la réponse de Vitaly et c'est que vous ne pouvez pas définir les variables que vous voulez être nique à la portée, si vous avez fait un nom privé de cette façon et que vous l'avez ensuite changé, la valeur du nom changerait pour chaque instance de la classe, il y a donc un moyen de résoudre ce problème
# 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
Il n'est pas impossible d'accéder au tableau des noms de l'extérieur sauf si vous utilisez getNames
Testez cela
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 compilé
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;
};
Voici une solution qui s'appuie sur plusieurs des autres réponses ici plus https://stackoverflow.com/a/7579956/148451 . Il stocke les variables d'instance privées (non statiques) dans un tableau de classe privée (statique) et utilise un ID d'objet pour savoir quel élément de ce tableau contient les données appartenant à chaque instance.
# 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
Voici comment déclarer des membres privés non statiques dans Coffeescript
Pour référence complète, vous pouvez consulter 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
"classe" dans les scripts de café conduit à un résultat basé sur un prototype. Ainsi, même si vous utilisez une variable privée, elle est partagée entre les instances. Tu peux le faire:
EventEmitter = ->
privateName = ""
setName: (name) -> privateName = name
getName: -> privateName
.. mène à
emitter1 = new EventEmitter()
emitter1.setName 'Name1'
emitter2 = new EventEmitter()
emitter2.setName 'Name2'
console.log emitter1.getName() # 'Name1'
console.log emitter2.getName() # 'Name2'
Mais attention à faire passer les membres privés avant les fonctions publiques, car le script café renvoie les fonctions publiques en tant qu'objet. Regardez le Javascript compilé:
EventEmitter = function() {
var privateName = "";
return {
setName: function(name) {
return privateName = name;
},
getName: function() {
return privateName;
}
};
};
Étant donné que le script Coffee se compile en JavaScript, la seule façon d'avoir des variables privées est de fermer.
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
Cela se compilera via le code JavaScript suivant:
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
Bien sûr, cela a les mêmes limites que toutes les autres variables privées que vous pouvez avoir grâce à l'utilisation de fermetures, par exemple, les méthodes nouvellement ajoutées n'y ont pas accès car elles n'étaient pas définies dans la même étendue.
Vous ne pouvez pas le faire facilement avec les classes CoffeeScript, car elles utilisent le modèle de constructeur Javascript pour créer des classes.
Cependant, vous pouvez dire quelque chose comme ceci:
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}"
Mais vous perdez la grandeur des classes CoffeeScript, car vous ne pouvez pas hériter d'une classe créée de cette manière autrement qu'en utilisant à nouveau extend (). instanceof ne fonctionnera plus et les objets créés de cette façon consomment un peu plus de mémoire. De plus, vous ne devez plus utiliser les mots-clés new et super.
Le fait est que les fermetures doivent être créées chaque fois qu'une classe est instanciée. Les fermetures de membres dans les classes CoffeeScript pures ne sont créées qu'une seule fois, c'est-à-dire lorsque le "type" d'exécution de classe est construit.