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

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

[文本/ JavaScript的]

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

如果我正在使用一个类,我可以解决这个问题:

[文本/ CoffeeScript的]

 "use strict" Function::trigger = (prop, getter, setter) -> Object.defineProperty @::, get: getter set: setter class Class property: '' @trigger 'getable', -> 'x' member: 0 

但是,如果我想直接对象上定义触发器, 而不使用defineProperty / defineProperty 。 我想做一些像(这不是这样工作 ):

[文本/ X-伪的CoffeeScript]

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

它的JavaScript工作没有任何问题,我不希望我的脚本退步,当我使用CoffeeScript。 没有一种方法可以像JavaScript / ECMAScript 一样方便吗? 谢谢。

不,现在不是

从CoffeeScript FAQ :

问:你会添加Xfunction依赖于平台的function吗?

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

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

我个人认为,getter&setter文字语法对于CoffeeScript来说是一个很好的selectfunction,因为defineProperty是ECMAScript标准的一部分 。 在JavaScript中获取getter&setter的需求可能是有问题的,但是你并不是因为它们的存在而被迫使用它们。


无论如何,正如你注意到的那样,实现一个方便的包装函数并不难,它为类声明调用Object.defineProperty 。 我个人会使用在这里build议的方法:

 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 

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

 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 

下面是另一种在CoffeeScript中用getter和setter定义属性的方法,它保持了一个相对干净的语法,而不添加任何东西到全局函数原型中(我不愿意这样做):

 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,宽度,高度)定义的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 

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

你也可以在直的JSON对象上使用Object.defineProperty。

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

在CoffeeScript中,get / set符号不能用于各种原因。 最大的是编译器没有被build立起来来考虑得到/设置的符号。

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

像@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。

然后,构build并导入为您的类configuration的两个函数:

 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 

另一种方法:

 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 

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

 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"