IT story

CoffeeScript의 비공개 멤버?

hot-time 2020. 9. 17. 18:54
반응형

CoffeeScript의 비공개 멤버?


누군가 CoffeeScript에서 비공개 비 정적 멤버를 만드는 방법을 알고 있습니까? 현재 저는 클래스 외부에서 사용해서는 안된다는 것을 명확히하기 위해 밑줄로 시작하는 공용 변수를 사용하는이 작업을 수행하고 있습니다.

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

  getName: -> @_name

클래스에 변수를 넣으면 정적 멤버가되지만 어떻게 비 정적으로 만들 수 있습니까? "멋지다"지 않고도 가능할까요?


"멋지다"지 않고도 가능할까요?

슬프게도, 당신은 멋져 야 합니다.

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

기억 "그냥 자바 스크립트입니다."


클래스는 함수일 뿐이므로 범위를 만듭니다. 이 범위 내에 정의 된 모든 것은 외부에서 볼 수 없습니다.

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

더 멋진 걸 보여주고 싶어

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

Vitaly의 대답에는 한 가지 문제가 있으며 범위에 고유 하고 싶은 변수를 정의 할 수 없다는 것입니다. 그렇게 개인 이름을 만든 다음 변경하면 이름 값이 클래스의 모든 인스턴스에 대해 변경됩니다. 그 문제를 해결할 수있는 한 가지 방법이 있습니다

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

컴파일 된 자바 스크립트

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

여기에 다른 몇 가지 답변과 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

여기 내가 설정에 대해 발견 된 최고의 기사 public static members, private static members, public and private members, 및 기타 관련 물건. 그것은 많은 정보와 커버 js대의 coffee비교. 그리고 역사적인 이유로 여기에 최고의 코드 예제가 있습니다.

# 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

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

커피 스크립트의 "클래스"는 프로토 타입 기반 결과로 이어집니다. 따라서 개인 변수를 사용하더라도 인스턴스간에 공유됩니다. 다음과 같이 할 수 있습니다.

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

Since coffee script compiles down to JavaScript the only way you can have private variables is through closures.

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

This will compile down through the following 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

Of course this has all the same limitations as all the other private variables you can have through the use of closures, for example, newly added methods don't have access to them since they were not defined in the same scope.


You can't do it easily with CoffeeScript classes, because they use the Javascript constructor pattern for creating classes.

However, you could say something like this:

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

But you lose the greatness of CoffeeScript classes, because you can't inherit from a class created that way by any other way than by using extend() again. instanceof will stop working, and objecs created this way consume a little bit more memory. Also, you mustn't use the new and super keywords anymore.

The point is, that the closures must be created every time a class is instantiated. The member closures in pure CoffeeScript classes are created only once - that is, when the class runtime "type" is constructed.


If you want only separate private memebers from public, just wrap it in $ variable

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

and use @$.requirements

참고URL : https://stackoverflow.com/questions/4685626/private-members-in-coffeescript

반응형