Alguém sabe como tornar membros privados e não estáticos no CoffeeScript? Atualmente estou fazendo isso, que usa apenas uma variável pública começando com um sublinhado para esclarecer que não deve ser usada fora da classe:
class Thing extends EventEmitter
constructor: (@_name) ->
getName: -> @_name
Colocar a variável na classe a torna um membro estático, mas como posso torná-la não estática? É mesmo possível sem ficar "chique"?
name
só é visível de dentro do fechamento do construtor. Olhe para esta essência: gist.github.com/803810@getName = -> name
parece quebrar qualquer herança possível dagetName
função.getName
é público ename
só pode ser acessado pela função do construtor, portanto, não é realmente "privado" para o objeto.classes são apenas funções, portanto, criam escopos. tudo definido dentro deste escopo não será visível de fora.
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 compila isso no seguinte:
(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);
fonte
foo.call(this)
, a fim dethis
ser exemplo da função. É por isso que tentar emular a herança clássica em JavaScript fica complicado.var
declarações internas que irão ocultá-los.Eu gostaria de mostrar algo ainda mais sofisticado
class Thing extends EventEmitter constructor: ( nm) -> _name = nm Object.defineProperty @, 'name', get: -> _name set: (val) -> _name = val enumerable: true configurable: true
Agora você pode fazer
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
fonte
Há um problema com a resposta de Vitaly: você não pode definir variáveis que deseja que sejam exclusivas para o escopo, se você criar um nome privado dessa forma e depois alterá-lo, o valor do nome mudará para cada instância da classe, então há uma maneira de resolver esse problema
# 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
Não é impossível acessar a matriz de nomes de fora, a menos que você use
getNames
Teste isso
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 compilado
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; };
fonte
Aqui está uma solução que se baseia em várias das outras respostas aqui, além de https://stackoverflow.com/a/7579956/1484513 . Ele armazena as variáveis de instância privada (não estáticas) em uma matriz de classe privada (estática) e usa um ID de objeto para saber qual elemento dessa matriz contém os dados pertencentes a cada instância.
# 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
fonte
Aqui é o melhor artigo que encontrei sobre o ajuste
public static members
,private static members
,public and private members
, e algumas outras coisas relacionadas. Ele cobre muitos detalhes ejs
vs.coffee
comparação. E por razões históricas , aqui está o melhor exemplo de código dele:# 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
fonte
Aqui está como você pode declarar membros privados e não estáticos no Coffeescript
Para referência completa, você pode dar uma olhada em 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
fonte
"classe" em scripts de café leva a um resultado baseado em protótipo. Portanto, mesmo se você usar uma variável privada, ela será compartilhada entre as instâncias. Você consegue fazer isso:
EventEmitter = -> privateName = "" setName: (name) -> privateName = name getName: -> privateName
.. leva a
emitter1 = new EventEmitter() emitter1.setName 'Name1' emitter2 = new EventEmitter() emitter2.setName 'Name2' console.log emitter1.getName() # 'Name1' console.log emitter2.getName() # 'Name2'
Mas tome cuidado para colocar os membros privados antes das funções públicas, porque o script de café retorna as funções públicas como objeto. Veja o Javascript compilado:
EventEmitter = function() { var privateName = ""; return { setName: function(name) { return privateName = name; }, getName: function() { return privateName; } }; };
fonte
Como o script de café é compilado em JavaScript, a única maneira de ter variáveis privadas é por meio de fechamentos.
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
Isso será compilado por meio do seguinte 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
É claro que isso tem as mesmas limitações que todas as outras variáveis privadas que você pode ter com o uso de fechamentos, por exemplo, métodos recém-adicionados não têm acesso a eles, pois não foram definidos no mesmo escopo.
fonte
e = new Animal(5);f = new Animal(1);e.test()
alerta um, quero cinco.Você não pode fazer isso facilmente com classes CoffeeScript, porque elas usam o padrão de construtor Javascript para criar classes.
No entanto, você pode dizer algo assim:
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}"
Mas você perde a grandeza das classes CoffeeScript, porque você não pode herdar de uma classe criada dessa forma de outra forma que não seja usando extend () novamente. instanceof irá parar de funcionar, e objetos criados desta forma consomem um pouco mais de memória. Além disso, você não deve usar mais as novas e super palavras - chave.
A questão é que os encerramentos devem ser criados sempre que uma classe é instanciada. Os fechamentos de membro em classes CoffeeScript puras são criados apenas uma vez - ou seja, quando o "tipo" de tempo de execução da classe é construído.
fonte
Se você quiser separar apenas os membros privados dos públicos, apenas envolva-os em $ variable
$: requirements: {} body: null definitions: null
E use
@$.requirements
fonte