Предлагаю вашему вниманию перевод статьи "Advanced Javascript Properties" с сайта jstips.co.
Можно настроить объекты в javascript так, чтобы, например, установить им свойства псевдо-private или readonly. Эта функция доступна начиная с ECMAScript 5.1, поэтому поддерживается всеми браузерами последних версий. Чтобы сделать это, вам необходимо использовать метод defineProperty для Object, например так:
Синтаксис выглядит так:
Или для нескольких свойств:
Где options включает следующие атрибуты:
value — если это не getter (см. ниже), то value обязательный атрибут. {a: 12} === Object.defineProperty(obj, 'a', {value: 12})
writable — устанавливает свойство в readonly. Обратите внимание, если свойство является вложенным, то оно доступно для редактирования.
enumerable — устанавливает свойство как скрытое. Это значит, что for...of и stringify не будут в своем результате выдавать его, хотя оно по прежнему существует. Примечание. Это не значит, что свойство становится приватным. Оно все так же доступно изнутри, просто не будет выводиться.
configurable — устанавливает свойство, как не изменяемое, например защищенное от удаления или предопределения. Опять же, если свойство вложенное, то его можно редактировать.
Так что для того, чтобы создать приватное постоянное свойство, вы должны определить его как:
Помимо настройки свойств, defineProperty может определять их динамически, благодаря второму параметру являющемуся строкой. Например, предположим, я хочу создать свойства в соответствии с какой-либо конфигурацией:
Но это не все! Дополнительные свойства позволяют создавать геттеры и сеттеры, подобно другим языкам ООП. Однако в этом случае нельзя использовать writable, enumerable и configurable
За исключением случаев очевидного преимущества инкапсуляции и расширенных аксессоров, можно заметить, что мы не «вызываем» геттер, а получаем его как свойство, без скобок! Это восхитительно! Например, давайте представим, что у нас есть объект с кучей вложенных свойств:
Теперь, вместо a.b.c[0].d (где одно из свойств может оказаться undefined и вывалить ошибку), мы можем создать алиас:
Если попытаться установить геттер без сеттера и попытаться задать value, вы получите ошибку. Это особенно важно при использовании вспомогательных функций, таких как $ .extend или _.merge. Будьте осторожны!
Можно настроить объекты в javascript так, чтобы, например, установить им свойства псевдо-private или readonly. Эта функция доступна начиная с ECMAScript 5.1, поэтому поддерживается всеми браузерами последних версий. Чтобы сделать это, вам необходимо использовать метод defineProperty для Object, например так:
var a = {};
Object.defineProperty(a, 'readonly', {
value: 15,
writable: false
});
a.readonly = 20;
console.log(a.readonly); // 15
Синтаксис выглядит так:
Object.defineProperty(dest, propName, options)
Или для нескольких свойств:
Object.defineProperties(dest, {
propA: optionsA,
propB: optionsB, //...
});
Где options включает следующие атрибуты:
value — если это не getter (см. ниже), то value обязательный атрибут. {a: 12} === Object.defineProperty(obj, 'a', {value: 12})
writable — устанавливает свойство в readonly. Обратите внимание, если свойство является вложенным, то оно доступно для редактирования.
enumerable — устанавливает свойство как скрытое. Это значит, что for...of и stringify не будут в своем результате выдавать его, хотя оно по прежнему существует. Примечание. Это не значит, что свойство становится приватным. Оно все так же доступно изнутри, просто не будет выводиться.
configurable — устанавливает свойство, как не изменяемое, например защищенное от удаления или предопределения. Опять же, если свойство вложенное, то его можно редактировать.
Так что для того, чтобы создать приватное постоянное свойство, вы должны определить его как:
Object.defineProperty(obj, 'myPrivateProp', {value: val, enumerable: false, writable: false, configurable: false});
Помимо настройки свойств, defineProperty может определять их динамически, благодаря второму параметру являющемуся строкой. Например, предположим, я хочу создать свойства в соответствии с какой-либо конфигурацией:
var obj = {
getTypeFromExternal(): true // illegal in ES5.1
}
Object.defineProperty(obj, getTypeFromExternal(), {value: true}); // ok
// For the example sake, ES6 introduced a new syntax:
var obj = {
[getTypeFromExternal()]: true
}
Но это не все! Дополнительные свойства позволяют создавать геттеры и сеттеры, подобно другим языкам ООП. Однако в этом случае нельзя использовать writable, enumerable и configurable
function Foobar () {
var _foo; // true private property
Object.defineProperty(obj, 'foo', {
get: function () { return _foo; }
set: function (value) { _foo = value }
});
}
var foobar = new Foobar();
foobar.foo; // 15
foobar.foo = 20; // _foo = 20
За исключением случаев очевидного преимущества инкапсуляции и расширенных аксессоров, можно заметить, что мы не «вызываем» геттер, а получаем его как свойство, без скобок! Это восхитительно! Например, давайте представим, что у нас есть объект с кучей вложенных свойств:
var obj = {a: {b: {c: [{d: 10}, {d: 20}] } } };
Теперь, вместо a.b.c[0].d (где одно из свойств может оказаться undefined и вывалить ошибку), мы можем создать алиас:
Object.defineProperty(obj, 'firstD', {
get: function () { return a && a.b && a.b.c && a.b.c[0] && a.b.c[0].d }
})
console.log(obj.firstD) // 10
Замечание
Если попытаться установить геттер без сеттера и попытаться задать value, вы получите ошибку. Это особенно важно при использовании вспомогательных функций, таких как $ .extend или _.merge. Будьте осторожны!
Ссылки
Комментарии (5)
Scalar
18.02.2016 17:37За исключением случаев очевидного преимущества инкапсуляции и прогрессивных асессоров, можно заметить, что мы не «вызываем» геттер, а получаем его как свойство без скобок
Автор, пожалуйста, доработайте перевод. В таком виде его очень и очень сложно читать.
dom1n1k
18.02.2016 22:42+1А что там говорят на лекциях про движок V8?
Вроде бы он отказывается оптимизировать геттеры и сеттеры?
Demogor
19.02.2016 00:38А вот вопрос — есть ли возможность изменять неизменяемые поля объектов, типа windows.location, без переопределения в локальной области видимости?
kahi4
На хабре, "современный учебник js", MDN, даже MSDN. Каждая из статей более подробна, чем у вас.
Value по-умолчанию undefined, а не обязательное.
Что? К слову, стоит упомянуть, что без 'use strickt' при попытке изменения исключение выброшено не будет.
В каком месте оно приватное? Оно по-прежнему доступно из-вне, только immutable.
Строго говоря, оно вообще только динамически их и определяет.
Только writable.
Чего?
P.S. Понял, что значит «вложенное свойство». Если это свойство имеет тип object — свойства самого этого объекта редактировать можно. Но это очевидно, покуда свойство хранит только ссылку на тот объект. Стоило написать понятнее.