Предлагаю вашему вниманию перевод статьи "Advanced Javascript Properties" с сайта jstips.co.

Можно настроить объекты в 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)


  1. kahi4
    18.02.2016 17:17
    +7

    На хабре, "современный учебник js", MDN, даже MSDN. Каждая из статей более подробна, чем у вас.

    value — если это не getter (см. ниже), то value обязательный атрибут.

    Value по-умолчанию undefined, а не обязательное.

    Обратите внимание, если свойство является вложенным, то оно доступно для редактирования.
    Что? К слову, стоит упомянуть, что без 'use strickt' при попытке изменения исключение выброшено не будет.

    Так что для того, чтобы создать приватное постоянное свойство, вы должны определить его как:

    В каком месте оно приватное? Оно по-прежнему доступно из-вне, только immutable.

    Помимо настройки свойств, defineProperty может определять их динамически

    Строго говоря, оно вообще только динамически их и определяет.

    Однако в этом случае нельзя использовать writable, enumerable и configurable

    Только writable.

    асессоров

    Чего?

    P.S. Понял, что значит «вложенное свойство». Если это свойство имеет тип object — свойства самого этого объекта редактировать можно. Но это очевидно, покуда свойство хранит только ссылку на тот объект. Стоило написать понятнее.


  1. Scalar
    18.02.2016 17:37

    За исключением случаев очевидного преимущества инкапсуляции и прогрессивных асессоров, можно заметить, что мы не «вызываем» геттер, а получаем его как свойство без скобок

    Автор, пожалуйста, доработайте перевод. В таком виде его очень и очень сложно читать.


  1. dom1n1k
    18.02.2016 22:42
    +1

    А что там говорят на лекциях про движок V8?
    Вроде бы он отказывается оптимизировать геттеры и сеттеры?


  1. Demogor
    19.02.2016 00:38

    А вот вопрос — есть ли возможность изменять неизменяемые поля объектов, типа windows.location, без переопределения в локальной области видимости?