web-dev-qa-db-ja.com

CoffeeScript:オブジェクト初期化子のゲッター/セッター

ECMAScriptでは、ゲッターまたはセッターを次のように定義できます。

[text/javascript]

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

クラスを使用している場合は回避できます:

[text/coffeescript]

"use strict"

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

class Class
      property: ''

      @trigger 'getable', ->
               'x'

      member: 0

しかし、オブジェクトに直接トリガーを定義したい場合はどうすればいいですか-without using defineProperty/-ies。私は(そのように動作しないのような)をしたい:

[text/x-pseudo-coffeescript]

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

JavaScriptで問題なく動作しており、CoffeeScriptを使用しているときにスクリプトが退行するのは望ましくありません。 JavaScriptのように快適に/ECMAScriptにする方法はありませんか?ありがとう。

41
fridojet

いいえ、今のところではありません:(

CoffeeScript FAQ から:

Q:機能Xがプラットフォームに依存する場合、機能Xを追加しますか?

A:いいえ、実装固有の機能はポリシーとして許可されていません。 CoffeeScriptで記述するすべてのものは、現在のJavaScript実装でサポートされ、実行可能である必要があります(実際には、これは最も一般的な分母がIE6であることを意味します)。したがって、次のような機能は実装されません。ゲッターとセッター、yield。

ゲッターとセッターの構文に関するGitHubの問題: #64#451#1165 (最後のニースでいくつかの議論があります)。

definePropertyECMAScript標準の一部 であるため、Getter&setterリテラル構文を持つことはCoffeeScriptのニースオプトイン機能になると個人的には考えています。 JavaScriptでのゲッターとセッターの必要性は疑わしい場合がありますが、それらが存在するという理由だけでそれらを使用することは強制されません。


とにかく、お気づきのとおり、クラス宣言のためにObject.definePropertyを呼び出す便利なラッパー関数を実装するのはそれほど難しくありません。私は個人的に here で提案されているアプローチを使用します:

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

または、2つの異なるメソッドを作成することもできます。

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

プレーンオブジェクトの場合は、オブジェクト自体でObject.defineProperty(または Object.defineProperties ;))を Jason提案 として使用できます。たぶんそれを小さな機能でラップする:

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
75
epidemian

CoffeeScriptでゲッターとセッターを使用してプロパティを定義する別のアプローチを次に示します。これは、グローバル関数プロトタイプに何も追加せずに比較的きれいな構文を維持します(私はそうしません)。

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

多くのプロパティでうまく機能します。たとえば、(x、y、幅、高さ)で定義されているが、代替表現(x1、y1、x2、y2)のアクセサーを提供するRectangleクラスは次のとおりです。

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コード です。楽しい!

29
curran

Object.definePropertyは、ストレートJSONオブジェクトでも使用できます。

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

CoffeeScriptでは、さまざまな理由でget/set表記が機能しません。最大の利点は、get/set表記を考慮してコンパイラが構築されていないことです。

Get/setはすべてのブラウザ(具体的にはIE)でサポートされているわけではないことに注意してください。また、新しいECMA標準(ECMAScript5)では、ゲッター/セッターでプロパティを定義する方法としてObject.definePropertyに言及していることに注意してください。

7
Jason L.

@curranと同様に、Functionプロトタイプを変更しないことを好みます。ここに私のプロジェクトの1つでやったことがあります:

特定のクラスに対して2つの関数を返すユーティリティ関数をどこかで定義すると、クラスのプロトタイプにゲッターとセッターを簡単に追加できます。

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

gsg etterおよびs etterを表します。

次に、クラス用に構成された2つの関数をビルドしてインポートします。

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
5
Mickaël Gauvin

前に行った他の人に感謝します。非常に一般的かつ簡単に:

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"
1
kwerle

別のアプローチ:

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
1
M K