【问题标题】:Private members in CoffeeScript?CoffeeScript 中的私人成员?
【发布时间】:2011-06-08 19:25:00
【问题描述】:

有人知道如何在 CoffeeScript 中创建私有的非静态成员吗​​?目前我正在这样做,它只是使用一个以下划线开头的公共变量来澄清它不应该在类之外使用:

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

  getName: -> @_name

将变量放入类中使其成为静态成员,但如何使其成为非静态成员?有没有可能不被“看中”?

【问题讨论】:

    标签: oop instance-variables private-members coffeescript


    【解决方案1】:

    类只是函数,因此它们创建了作用域。在此范围内定义的所有内容都不会从外部可见。

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

    【讨论】:

    • 需要注意的是,这些私有变量可用于子类。
    • 还应该注意,需要像foo.call(this) 一样调用“私有”方法,以便this 成为函数的实例。这就是为什么试图在 JavaScript 中模拟经典继承变得棘手的原因。
    • 另一个缺点是您无法使用“私有”方法进行单元测试..
    • @nuc 私有方法是通过调用它们的公共方法进行测试的实现细节,也就是说私有方法不应该进行单元测试。如果私有方法看起来应该是可单元测试的,那么也许它应该是公共方法。请参阅这篇文章以获得很好的解释stackoverflow.com/questions/5750279/…
    • 还应该注意的是,您需要在“公共”函数中使用它们的地方定义您的“私有”变量。否则,CoffeeScript 会感到困惑并创建新的内部 var 声明,这将影响它们。
    【解决方案2】:

    有没有可能不被“看中”?

    遗憾的是,你必须是 fancy

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

    记住,“这只是 JavaScript。”

    【讨论】:

    • ...所以你必须像在 JS 中那样做。当它隐藏在所有的糖后面时很容易忘记它,谢谢!
    • 这真的是私人的吗?您仍然可以在课堂外访问它。 a=Thing('a') 然后 a.getName() 返回值并 a.getName = ->'b' 设置它。
    • @Amir: name 仅在构造函数闭包内部可见。看看这个要点:gist.github.com/803810
    • 另外值得注意的是@getName = -> name似乎破坏了getName函数的任何可能继承。
    • 这个答案是错误的:这里,getName 是公共的,而name 只能从构造函数访问,所以它不是真正的对象“私有”。
    【解决方案3】:

    我想展示一些更漂亮的东西

    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
    

    【讨论】:

      【解决方案4】:

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

      编译好的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;
      };
      

      【讨论】:

      • 我喜欢这个实现。有什么缺点吗?
      【解决方案5】:

      这是一个利用此处其他几个答案以及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
      

      【讨论】:

        【解决方案6】:

        Here's 我找到的关于设置public static membersprivate static memberspublic and private members 和其他一些相关内容的最佳文章。它涵盖了很多细节和jscoffee 的比较。出于历史的原因,这里有最好的代码示例:

        # 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
        

        【讨论】:

          【解决方案7】:

          这是在 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
          

          【讨论】:

            【解决方案8】:

            咖啡脚本中的“class”导致基于原型的结果。因此,即使您使用私有变量,它也会在实例之间共享。你可以这样做:

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

            【讨论】:

              【解决方案9】:

              由于咖啡脚本编译为 JavaScript,因此拥有私有变量的唯一方法是通过闭包。

              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
              

              当然,这与您可以通过使用闭包获得的所有其他私有变量具有所有相同的限制,例如,新添加的方法无法访问它们,因为它们未在同一范围内定义。

              【讨论】:

              • 这是一种静态成员。 e = new Animal(5);f = new Animal(1);e.test() 提醒一个,我想要五个。
              • @thejh 哦,对不起,我现在看到了错误,估计昨天想这些东西已经太晚了。
              • @thejh 这发生在我身上,我试图在我的回答中解决这个问题。
              【解决方案10】:

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

              但是你失去了 CoffeeScript 类的伟大之处,因为除了再次使用 extend() 之外,你不能从以这种方式创建的类继承。 instanceof 将停止工作,并且以这种方式创建的对象会消耗更多内存。此外,您不能再使用 newsuper 关键字。

              关键是,每次实例化类时都必须创建闭包。纯 CoffeeScript 类中的成员闭包只创建一次 - 即在构造类运行时“类型”时。

              【讨论】:

                【解决方案11】:

                如果您只想将私有成员与公共成员分开,只需将其包装在 $ 变量中

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

                并使用@$.requirements

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2020-11-15
                  • 1970-01-01
                  • 1970-01-01
                  • 2013-02-13
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多