CoffeeScript:对象初始化器中的Getter/Setter

fri*_*jet 42 javascript oop getter-setter coffeescript ecmascript-5

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

[文本/ JavaScript的]

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

如果我正在使用课程,我可以解决:

[文本/ CoffeeScript的]

"use strict"

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

class Class
      property: ''

      @trigger 'getable', ->
               'x'

      member: 0
Run Code Online (Sandbox Code Playgroud)

但是,如果我想要什么定义上触发直接对象 - 使用defineProperty/ - ies.我想做一些事情(它不是那样工作):

[文本/ X-伪的CoffeeScript]

object =
  property: 'xhr'
  get getable: 'x'
Run Code Online (Sandbox Code Playgroud)

它在JavaScript中运行没有任何问题,我不希望我的脚本在使用CoffeeScript时回退.难道没有办法像JavaScript/ECMAScript 一样舒服吗?谢谢.

epi*_*ian 76

不,不是现在:(

来自CoffeeScript FAQ:

问:您是否会在功能X依赖于平台时添加功能X?

答:不可以,特定于实现的功能不允许作为策略.您在CoffeeScript中编写的所有内容都应该在任何当前的JavaScript实现上得到支持和运行(实际上,这意味着最低的共同点是IE6).因此,将不会实现以下功能:getters&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
Run Code Online (Sandbox Code Playgroud)

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

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 ' '
Run Code Online (Sandbox Code Playgroud)

对于普通对象,您可以像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
Run Code Online (Sandbox Code Playgroud)

  • 这里有一个使用属性的派生类中的问题.`super`表现不像你期望的那样.没什么大不了的,但值得指出.请参阅https://gist.github.com/4236746 (3认同)

cur*_*ran 30

这是使用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
Run Code Online (Sandbox Code Playgroud)

它适用于许多属性.例如,这是一个以(x,y,width,height)定义的Rectangle类,但为替代表示(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
Run Code Online (Sandbox Code Playgroud)

这是相应的JavaScript代码.请享用!

  • 这太棒了!有用!!我喜欢CoffeeScript,但它本身不支持STUPID getter/setter. (2认同)

Jas*_* L. 8

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

obj = {}
Object.defineProperty obj, 'foo',
    get: ->
        return 'bar'
Run Code Online (Sandbox Code Playgroud)

在CoffeeScript中,get/set表示法因各种原因不起作用.最大的问题是编译器尚未构建以考虑get/set表示法.

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


Mic*_*vin 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
Run Code Online (Sandbox Code Playgroud)

GS代表埃特和小号埃特。

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

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
Run Code Online (Sandbox Code Playgroud)