CoffeeScript: Getter/Setter в инициализаторах объектов
ECMAScript позволяет определить геттеры или сеттеры следующим образом:
[текст/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
/- ies
. Я хочу сделать что-то вроде (это не работает):
[текст/х-псевдо-CoffeeScript]
object =
property: 'xhr'
get getable: 'x'
Он работает в JavaScript без каких-либо проблем, и я не хочу, чтобы мои скрипты регрессировали, когда я использую CoffeeScript. Нет ли способа сделать это так же комфортно, как в JavaScript/ECMAScript? Спасибо.
Ответы
Ответ 1
Нет, не сейчас: (
Из Часто задаваемые вопросы о CoffeeScript:
Q: Вы добавите функцию X, где функция X зависит от платформы?
A: Нет, специфические для реализации функции не допускаются как политика. Все, что вы пишете в CoffeeScript, должно поддерживаться и выполняться при любой текущей реализации JavaScript (на практике это означает, что самым низким общим знаменателем является IE6). Таким образом, такие функции, как следующие, не будут реализованы: getters и seters, yield.
Некоторые проблемы GitHub о синтаксисе getter и setter: # 64, # 451, # 1165 (в последнем есть приятное обсуждение).
Я лично считаю, что наличие синтаксиса getter и setter literal было бы хорошей функцией выбора для CoffeeScript теперь, когда defineProperty
часть стандарта ECMAScript. Потребность в геттерах и сеттерах в JavaScript может быть сомнительной, но вы не вынуждены использовать их только потому, что они существуют.
В любом случае, как вы заметили, не так сложно реализовать удобную функцию-оболочку, которая вызывает 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 ' '
Для простых объектов вы можете просто использовать 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
Ответ 2
Вот еще один подход для определения свойств с геттерами и сеттерами в 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
Он хорошо работает со многими свойствами. Например, здесь класс 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. Наслаждайтесь!
Ответ 3
Вы можете использовать Object.defineProperty для прямых объектов JSON.
obj = {}
Object.defineProperty obj, 'foo',
get: ->
return 'bar'
Обозначение get/set не работает по различным причинам в CoffeeScript. Самое большое существо, что компилятор не был создан для учета ввода/установки.
Обратите внимание, что get/set не поддерживается всеми браузерами (в частности, IE). Также обратите внимание, что в новых стандартах ECMA (ECMAScript5) упоминается Object.defineProperty как способ определения свойств с помощью getters/seters.
Ответ 4
Как @curran, я предпочитаю не модифицировать прототип Function
.
Вот что я сделал в одном из моих проектов:
Определите где-нибудь функцию утилиты, которая для данного класса возвращает 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
gs обозначает g etter и s.
Затем вы создаете и импортируете две функции, настроенные для вашего класса:
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
Альтернативный подход:
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
Ответ 6
Спасибо другим, которые ушли раньше. Очень просто и просто:
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"