43

ECMAScript 允许我们定义 getter 或 setter 如下:

[文本/javascript]

var object = {
  property: 7,
  get getable() { return this.property + 1; },
  set setable(x) { this.property = x / 2; }
};

如果我使用,我可以解决:

[文本/咖啡脚本]

"use strict"

Function::trigger = (prop, getter, setter) ->
      Object.defineProperty @::,
              get: getter
              set: setter               

class Class
      property: ''

      @trigger 'getable', ->
               'x'

      member: 0

但是,如果我想直接在对象上定义触发器怎么办-使用defineProperty/ -ies。我想做类似的事情(它不是那样工作的):

[文本/x-伪咖啡脚本]

object =
  property: 'xhr'
  get getable: 'x'

它在 JavaScript 中运行没有任何问题,我不希望我的脚本在使用 CoffeeScript 时倒退。难道没有一种方法可以像在 JavaScript /ECMAScript中那样舒服吗?谢谢。

4

6 回答 6

78

不,暂时没有:(

来自CoffeeScript 常见问题解答

问:您会在功能 X 依赖于平台的情况下添加功能 X 吗?

答:不可以,不允许将特定于实现的功能作为策略。您在 CoffeeScript 中编写的所有内容都应该在任何当前的 JavaScript 实现上得到支持和运行(实际上,这意味着最低的公分母是 IE6)。因此,将不会实现以下功能:getter & setter、yield。

一些关于 getter 和 setter 语法的 GitHub 问题:#64#451#1165(最后一个有一些很好的讨论)。

我个人认为拥有 getter 和 setter 文字语法对于 CoffeeScript 来说是一个不错的选择加入功能,因为它definePropertyECMAScript 标准的一部分。JavaScript 中对 getter 和 setter 的需求可能值得怀疑,但您不会因为它们存在而被迫使用它们。


无论如何,正如您所注意到的,实现一个调用Object.defineProperty类声明的便捷包装函数并不难。我个人会使用这里建议的方法:

Function::property = (prop, desc) ->
  Object.defineProperty @prototype, prop, desc

class Person
  constructor: (@firstName, @lastName) ->
  @property 'fullName',
    get: -> "#{@firstName} #{@lastName}"
    set: (name) -> [@firstName, @lastName] = name.split ' '

p = new Person 'Robert', 'Paulson'
console.log p.fullName # Robert Paulson
p.fullName = 'Space Monkey'
console.log p.lastName # Monkey

或者,也许创建两种不同的方法:

Function::getter = (prop, get) ->
  Object.defineProperty @prototype, prop, {get, configurable: yes}

Function::setter = (prop, set) ->
  Object.defineProperty @prototype, prop, {set, configurable: yes}

class Person
  constructor: (@firstName, @lastName) ->
  @getter 'fullName', -> "#{@firstName} #{@lastName}"
  @setter 'fullName', (name) -> [@firstName, @lastName] = name.split ' '

对于普通对象,您可以按照Jason 的建议Object.defineProperty在对象本身上使用(或Object.defineProperties;))。也许将其包装在一个小函数中:

objectWithProperties = (obj) ->
  if obj.properties
    Object.defineProperties obj, obj.properties
    delete obj.properties
  obj

rectangle = objectWithProperties
  width: 4
  height: 3
  properties:
    area:
      get: -> @width * @height

console.log rectangle.area # 12
rectangle.width = 5
console.log rectangle.area # 15
于 2012-07-21T14:09:10.960 回答
33

这是另一种在 CoffeeScript 中使用 getter 和 setter 定义属性的方法,它保持了相对简洁的语法,而无需向全局 Function 原型添加任何内容(我不想这样做):

class Person
  constructor: (@firstName, @lastName) ->
  Object.defineProperties @prototype,
    fullName:
      get: -> "#{@firstName} #{@lastName}"
      set: (name) -> [@firstName, @lastName] = name.split ' '

p = new Person 'Robert', 'Paulson'
console.log p.fullName # Robert Paulson
p.fullName = 'Space Monkey'
console.log p.lastName # Monkey

它适用于许多属性。例如,这里有一个 Rectangle 类,它是根据 (x, y, width, height) 定义的,但为替代表示 (x1, y1, x2, y2) 提供了访问器:

class Rectangle                                     
  constructor: (@x, @y, @w, @h) ->
  Object.defineProperties @prototype,
    x1:
      get: -> @x
      set: (@x) ->
    x2:
      get: -> @x + @w
      set: (x2) -> @w = x2 - @x
    y1:
      get: -> @y
      set: (@y) ->
    y2:
      get: -> @y + @h
      set: (y2) -> @w = y2 - @y

r = new Rectangle 5, 6, 10, 11
console.log r.x2 # 15

这是相应的 JavaScript 代码。享受!

于 2013-03-19T19:48:57.847 回答
8

您也可以在直接 JSON 对象上使用 Object.defineProperty。

obj = {}
Object.defineProperty obj, 'foo',
    get: ->
        return 'bar'

由于各种原因,CoffeeScript 中的 get/set 表示法不起作用。最大的问题是编译器没有被构建来解释 get/set 表示法。

请注意,并非所有浏览器(特别是 IE)都支持 get/set。另请注意,新的 ECMA 标准 (ECMAScript5) 提到 Object.defineProperty 作为使用 getter/setter 定义属性的方式。

于 2012-07-20T21:42:08.983 回答
5

像@curran 一样,我不喜欢修改Function原型。这是我在我的一个项目中所做的:

在某处定义一个实用函数,该函数对于给定的类返回 2 个函数,使您可以轻松地在类的原型上添加 getter 和 setter:

gs = (obj) ->
  getter: (propName, getterFunction) ->
    Object.defineProperty obj.prototype, propName, 
      get: getterFunction
      configurable: true
      enumerable: true
  setter: (propName, setterFunction) ->
    Object.defineProperty obj.prototype, propName, 
      set: setterFunction
      configurable: true
      enumerable: true

gs代表g etter 和s etter。

然后,您构建并导入为您的类配置的两个函数:

class Dog
  { getter, setter } = gs @

  constructor: (name, age) -> 
    @_name = name
    @_age = age

  getter 'name', -> @_name
  setter 'name', (name) -> 
    @_name = name
    return

  getter 'age', -> @_age
  setter 'age', (age) -> 
    @_age = age
    return
于 2015-03-10T20:24:35.730 回答
1

另一种方法:

get = (self, name, getter) ->
  Object.defineProperty self, name, {get: getter}

set = (self, name, setter) ->
  Object.defineProperty self, name, {set: setter}

prop = (self, name, {get, set}) ->
  Object.defineProperty self, name, {get: get, set: set}

class Demo 
  constructor: (val1, val2, val3) ->
    # getter only
    get @, 'val1', -> val1
    # setter only
    set @, 'val2', (val) -> val2 = val
    # getter and setter
    prop @, 'val3', 
      get: -> val3
      set: (val) -> val3 = val
于 2016-11-21T09:01:29.063 回答
1

感谢之前去过的其他人。非常普遍和简单:

attribute = (self, name, getterSetterHash) ->
  Object.defineProperty self, name, getterSetterHash

class MyClass
  constructor: () ->
    attribute @, 'foo',
      get: -> @_foo ||= 'Foo' # Set the default value
      set: (who) -> @_foo = "Foo #{who}"

    attribute @, 'bar',
      get: -> @_bar ||= 'Bar'

    attribute @, 'baz',
      set: (who) -> @_baz = who


myClass = new MyClass()
alert(myClass.foo) # alerts "Foo"
myClass.foo = 'me' # uses the foo setter
alert(myClass.foo) # alerts "Foo me"
于 2017-03-16T21:57:08.120 回答