Я знаю, как создавать геттеры и сеттеры для свойств, имена которых уже известны, делая что-то вроде этого:
// A trivial example:
function MyObject(val){
this.count = 0;
this.value = val;
}
MyObject.prototype = {
get value(){
return this.count < 2 ? "Go away" : this._value;
},
set value(val){
this._value = val + (++this.count);
}
};
var a = new MyObject('foo');
alert(a.value); // --> "Go away"
a.value = 'bar';
alert(a.value); // --> "bar2"
Теперь мой вопрос: можно ли определить такие универсальные геттеры и сеттеры? Т.е. создайте методы получения и установки для любого имени свойства, которое еще не определено.
Понятие возможно в PHP с использованием __get()
и __set()
методов магии (см РНР документации для получения информации о них), так что я действительно спрашивает, есть эквивалент JavaScript для них?
Излишне говорить, что в идеале мне хотелось бы кроссбраузерное решение.
Ответы:
Обновление 2013 и 2015 гг. (См. Ниже исходный ответ от 2011 г.) :
Это изменилось в спецификации ES2015 (также известной как «ES6»): теперь у JavaScript есть прокси . Прокси-серверы позволяют создавать объекты, которые являются настоящими заместителями (фасадами) других объектов. Вот простой пример, который при извлечении переводит все значения свойств, которые являются строками, заглавными буквами:
Операции, которые вы не отменяете, имеют поведение по умолчанию. Все, что мы переопределяем выше, есть
get
, но есть целый список операций, к которым вы можете подключиться.В
get
списке аргументов функции-обработчика:target
проксируемый объект (original
в нашем случае).name
- это (конечно) имя извлекаемого свойства, которое обычно является строкой, но также может быть символом.receiver
- это объект, который следует использовать, какthis
в функции получения, если свойство является средством доступа, а не свойством данных. В обычном случае это прокси или что-то, что от него наследуется, но это может быть что угодно, поскольку ловушка может быть вызванаReflect.get
.Это позволяет вам создать объект с желаемой функцией универсального получения и установки:
Результат вышеизложенного:
Обратите внимание на то, как мы получаем сообщение «несуществующее», когда пытаемся извлечь,
foo
когда оно еще не существует, и снова, когда мы его создаем, но не после этого.Ответ от 2011 года (см. Выше обновления за 2013 и 2015 годы) :
Нет, в JavaScript нет универсальной функции свойств. Синтаксис средства доступа, который вы используете, описан в Разделе 11.1.5 спецификации и не предлагает подстановочных знаков или чего-то подобного.
Вы, конечно, можете реализовать функцию для этого, но я предполагаю, что вы, вероятно, не хотите использовать
f = obj.prop("foo");
вместоf = obj.foo;
иobj.prop("foo", value);
вместоobj.foo = value;
(что было бы необходимо для функции для обработки неизвестных свойств).FWIW, функция получения (я не заморачивался с логикой установки) будет выглядеть примерно так:
Но опять же, я не могу представить, что вы действительно захотите это сделать, потому что это меняет способ использования объекта.
источник
Proxy
:Object.defineProperty()
. Я изложил подробности в своем новом ответе .Следующее может быть оригинальным подходом к этой проблеме:
Чтобы использовать его, свойства должны быть переданы в виде строк. Вот пример того, как это работает:
Изменить: улучшенный, более объектно-ориентированный подход, основанный на том, что я предложил, заключается в следующем:
Вы можете увидеть это здесь .
источник
Предисловие:
В ответе TJ Crowder упоминается a
Proxy
, который понадобится для универсального получателя / установщика для свойств, которые не существуют, как просил OP. В зависимости от того, какое поведение на самомProxy
деле требуется от динамических геттеров / сеттеров, в действительности может не потребоваться; или, возможно, вы можете захотеть использовать комбинациюProxy
с тем, что я покажу вам ниже.(PS Я недавно
Proxy
тщательно экспериментировал с Firefox на Linux и обнаружил, что он очень эффективен, но также несколько сбивает с толку / труден для работы и исправления. Что еще более важно, я также обнаружил, что он довольно медленный (по крайней мере, в относительно того, насколько оптимизирован JavaScript в наши дни) - я говорю в области дека-кратных медленнее.)Для конкретной реализации динамически создаваемых геттеров и сеттеров можно использовать
Object.defineProperty()
илиObject.defineProperties()
. Это тоже довольно быстро.Суть в том, что вы можете определить геттер и / или сеттер для объекта следующим образом:
Здесь следует отметить несколько моментов:
value
свойство в дескрипторе свойства ( не показано выше) одновременно сget
и / илиset
; из документов:val
свойство вне этогоObject.defineProperty()
дескриптора вызова / свойства. Это стандартное поведение.writable
чтобыtrue
в дескрипторе собственности , если вы используетеget
илиset
.configurable
иenumerable
, однако, в зависимости от того, что вам нужно; из документов:В этой связи они также могут быть интересны:
Object.getOwnPropertyNames(obj)
: получает все свойства объекта, даже неперечислимые (AFAIK, это единственный способ сделать это!).Object.getOwnPropertyDescriptor(obj, prop)
: получает дескриптор свойства объекта, объекта, который был переданObject.defineProperty()
выше.obj.propertyIsEnumerable(prop);
: для отдельного свойства в конкретном экземпляре объекта вызовите эту функцию в экземпляре объекта, чтобы определить, является ли конкретное свойство перечислимым или нет.источник
__get
и__set
.defineProperty
не занимается этим делом. Из вопроса: «Т.е. создавать геттеры и сеттеры для любого имени свойства, которое еще не определено». (их курсив).defineProperty
заранее определяет свойства. Единственный способ сделать то, что просил OP, - это прокси.obj.whateverProperty
, чтобы библиотека могла перехватить его с помощью универсального метода получения и присвоить имени свойства пользователь пытался получить доступ. Отсюда требование к «универсальным геттерам и сеттерам».это работает для меня
источник
Function()
похоже на использованиеeval
. Просто укажите функции как параметрыdefineProperty
. Или, если по какой-то причине вы настаиваете на динамическом созданииget
иset
, затем используйте функцию высокого порядка, которая создает функцию и возвращает ее, напримерvar get = (function(propName) { return function() { return this[propName]; };})('value');