CoffeeScript封装和变量访问

14

我试图理解CoffeeScript实例和类变量的工作原理,写下了这段代码(结果在注释中)。

class A
  x: 1
  @y: 2

  constructor: (@z) -> 
    #console.log "const x", x #ReferenceError: x is not defined
    console.log "constructor y", @y #undefined
    console.log "constructor z", @z # = 3 for A and 6 for B

  get: () -> 
    #console.log "get x", x #ReferenceError: x is not defined
    console.log "get y", @y #undefined
    console.log "get z", @z # = 3 for A and 6 for B

  get2: () => 
    #console.log "get2 x", x #ReferenceError: x is not defined
    console.log "get2 y", @y #undefined
    console.log "get2 z", @z # = 3 for A and 6 for B

  @get3: () -> 
    #console.log "get3 x", x #ReferenceError: x is not defined
    console.log "get3 y", @y # = 2
    console.log "get3 z", @z #undefined

  @get4: () => 
    #console.log "get4 x", x #ReferenceError: x is not defined
    console.log "get4 y", @y # = 2
    console.log "get4 z", @z #undefined

class B extends A
  constructor: (@w) ->
    super(@w)

console.log '------A------'
i = new A 3
console.log "i.x", i.x # = 1
console.log "i.y", i.y #undefined
console.log "i.z", i.z # = 6
i.get()
i.get2()
A.get3()
A.get4()
console.log '------B------'
i = new B 6
console.log "i.x", i.x # = 1
console.log "i.y", i.y #undefined
console.log "i.z", i.z # = 6
console.log "i.w", i.w # = 6
i.get()
i.get2()
B.get3()
B.get4()
console.log '------------'

这里发生了一些奇怪的事情:

  1. x变量 我希望能够从任何方法中访问它,但是无法从任何方法或构造函数中访问x变量(出现ReferenceError错误)。我只能从A或B的实例(i.x)中访问它。为什么会这样?

  2. @y变量 我希望可以从任何方法中获取@y变量的值,但在大多数地方它没有值(undefined值,不是ReferenceError异常)。@y只在@get3和@get4(实例方法?)中有值。如果已定义,为什么我不能获取其值?

  3. @y和@z变量 @y和@z都是实例变量,但由于@z在构造函数中初始化,因此具有不同的行为。@y在@get3和@get4上有效,而@z在get和get2上有效。再次,这里发生了什么?

问题在于我对这些行为感到非常困惑。这段代码是否正确?那么,我应该学习更多关于由CS生成的JS吗?

谢谢

2个回答

16
在函数体中,@引用的是this;而在类定义中,@则指代该类本身,而非它的原型。
因此,在上面的示例中,@y 的定义指代的是 A.y,而不是 A.prototype.y。由于不同方法定义方式中绑定this的方式不同,要想访问@可能会有些棘手。但是,如果在以@get命名的方法中使用@y,就可以随时引用A
另外,x的定义指代的是A.prototype.x,所以在get1get2的方法中,应该通过@x来访问它。
作为基本指南,请尽量避免在函数体外使用@,这样一切都会更加清晰明了。
class A
  constructor: (@a) ->
  b: 2
  tryStuff: =>
    console.log(@a) #will log whatever you initialized in the constructor
    console.log(@b) #will log 2

编辑:您可以将定义为@something的方法视为该类的静态方法,因此可以使用classname.something()调用它们,但作为静态方法,它们无法访问任何实例变量。


太好了!我认为最重要的是这里:“作为基本准则,尽量不要在函数体外使用 @,这样一切都会更有意义。”没错! - David Lojudice Sb.

9
回答你的问题:
1. 在类体内部,x = 1会在作用域中创建一个名为x的变量。但是在类体内部,x: 1会在原型上定义一个属性x。如果你将console.log x改为console.log @x,那么你将得到1
2. 在类体内部,@指向类本身。在构造函数中(以及作为instance.method调用的方法中),它指向特定的实例。将console.log @y改为console.log A.y,你将得到2。(你也可以使用@constructor从实例中获取对类的引用,因为在JavaScript中,类实际上就是构造函数。)
3. 由于构造函数中的@指向实例,所以你正在设置实例的z属性为给定值。
是的,我建议理解底层的JavaScript语言 - 我知道@有这么多不同的含义可能有点奇怪,但一旦你理解了JavaScript的this(这是语言中比较棘手的部分之一),它就会变得非常合理。顺便说一下,我的书中有更多关于这方面的信息。

1
谢谢!顺便说一下,我已经预订了你的书:D 希望能尽快收到! - David Lojudice Sb.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接