CoffeeScript의 비공개 멤버?


84

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

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

  getName: -> @_name

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

답변:


20

"멋지다"지 않고도 가능합니까?

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

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

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


1
... 그래서 JS에서하는 것처럼해야합니다. 모든 설탕 뒤에 숨겨져 있으면 잊기 쉽습니다. 감사합니다!
thejh

4
정말 사적인가요? 수업 외부에서도 계속 액세스 할 수 있습니다. a = Thing ( 'a') 그러면 a.getName ()이 값을 반환하고 a.getName =-> 'b'가 값을 설정합니다.
Amir

4
@Amir : name는 생성자 클로저 내부에서만 볼 수 있습니다. 이 요점을보십시오 : gist.github.com/803810
thejh

13
또한 함수의 @getName = -> name가능한 상속을 깨는 것 같습니다 getName.
Kendall Hopkins

12
이 대답은 잘못된 것입니다. 여기에 getName공개 name되어 있으며 생성자 함수에서만 액세스 할 수 있으므로 개체에 대해 실제로 "개인"이 아닙니다.
tothemario

203

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

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

9
이러한 개인 변수는 서브 클래스에서 사용할 수 없습니다 .
Ceasar Bautista

45
또한 '개인'방법처럼 호출 할 필요가 있음을 주목해야한다 foo.call(this)위해서는 this함수의 인스턴스가 될 수 있습니다. 이것이 자바 스크립트에서 고전적인 상속을 에뮬레이트하는 것이 어려운 이유입니다.
Jon Wingfield

3
또 다른 단점은 단위 테스트를위한 "비공개"방법에 액세스 할 수 없다는 것입니다.
nuc

16
@nuc 개인 메서드는이를 호출하는 공용 메서드를 통해 테스트되는 구현 세부 정보입니다. 즉, 개인 메서드는 단위 테스트가되지 않아야합니다. 개인 메서드가 단위 테스트가 가능해야하는 것처럼 보이면 공용 메서드 여야합니다. 좋은 설명은이 게시물을 참조하십시오. stackoverflow.com/questions/5750279/…
mkelley33

2
또한 "공용"함수에서 사용되는 위치 위에 "개인"변수를 정의해야합니다. 그렇지 않으면 CoffeeScript가 혼란스러워서이를 숨기는 새로운 내부 var선언을 만듭니다 .
Andrew Miner 2014

11

더 멋진 걸 보여주고 싶어

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

2

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

저는이 구현을 좋아합니다. 단점이 있습니까?
Erik5388

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

2

여기 내가 설정에 대해 발견 된 최고의 기사 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

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

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

0

커피 스크립트는 자바 스크립트로 컴파일되기 때문에 비공개 변수를 가질 수있는 유일한 방법은 클로저를 통하는 것입니다.

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

물론 이것은 클로저 사용을 통해 가질 수있는 다른 모든 private 변수와 동일한 제한을 가지고 있습니다. 예를 들어 새로 추가 된 메서드는 동일한 범위에 정의되지 않았기 때문에 액세스 할 수 없습니다.


9
그것은 일종의 정적 멤버입니다. e = new Animal(5);f = new Animal(1);e.test()알림 1 개, 5 개 원합니다.
thejh

@thejh 아, 죄송합니다. 이제 오류가 표시됩니다. 어제이 문제에 대해 생각하기에는 너무 늦었 네요.
Ivo Wetzel 2011 년

@thejh 나에게 일어난 일, 나는 내 대답에서 그 문제를 해결하려고 시도했습니다.
iConnor

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

그러나 extend ()를 다시 사용하는 것 외에는 다른 방법으로 생성 된 클래스에서 상속 할 수 없기 때문에 CoffeeScript 클래스의 위대함을 잃게됩니다. instanceof 는 작동을 멈추고 이렇게 생성 된 objecs는 조금 더 많은 메모리를 소비합니다. 또한 더 이상 newsuper 키워드를 사용하지 않아야합니다 .

요점은 클래스가 인스턴스화 될 때마다 클로저가 생성되어야한다는 것입니다. 순수 CoffeeScript 클래스의 멤버 클로저는 한 번만 생성됩니다. 즉, 클래스 런타임 "유형"이 생성 될 때 생성됩니다.


-3

공개 회원과 비공개 회원 만 분리하려면 $ 변수로 감싸세요.

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

그리고 사용 @$.requirements

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.