0

Преимущества не перечисляемых свойств в JavaScript

5

Энумерация является одной из трех характеристик свойства: запись, энумерация и конфигурируемость. У меня есть несколько вопросов:

  • В чем преимущества создания свойств неэнумерируемыми в JavaScript? Я понимаю, что мы скрываем свойство, делая его неэнумерируемым, но какие выгоды от скрытия свойств?
  • Можем ли мы получить доступ к неэнумерируемым свойствам? Если да, то в чем выгода от их неэнумерабельности?
  • Все ли предопределенные свойства объектов установлены как неэнумерируемые? Например, являются ли такие методы массива, как pop и push, неэнумерируемыми?

4 ответ(ов)

0

Основное преимущество использования Object.defineProperty заключается в том, что вы можете контролировать, какие свойства будут отображаться при перечислении свойств объекта, например, при использовании конструкции for in или метода Object.keys().

MDN хорошо объясняет это с помощью Object.defineProperty: https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

Обычно, когда разработчики хотят добавить метод к Object, например, полифил для некоторого метода, не поддерживаемого старыми браузерами, они изменяют свойство .prototype. Однако это делает новое свойство перечисляемым, и тем самым может осложнить результат, получаемый в циклах или при сборе ключей (особенно без использования .hasOwnProperty, который не все применяют).

Таким образом, вместо кода вроде:

Object.prototype.myMethod = function () {
    alert("Ах!");
};

вы можете воспользоваться Object.defineProperty, чтобы явно указать, что данный метод не должен быть перечисляемым:

Object.defineProperty(Object.prototype, 'myMethod', {
    value: function () {
        alert("Ах!");
    },
    enumerable: false
});

Таким образом, например, когда вы используете for (var key in obj), "myMethod" не будет включен в перечисление, и вам не придется беспокоиться о необходимости использования .hasOwnProperty. Основная проблема в этом заключается в том, что некоторые браузеры могут не поддерживать эту функциональность: http://kangax.github.com/es5-compat-table/, и не все библиотеки или код используют её, поэтому вы не всегда можете полагаться на внешние библиотеки или код, которые корректно работают с этим.

Вы можете в любой момент получить доступ к не перечисляемому свойству, оно просто не будет показано при перечислении свойств объекта — это ключевой момент.

Я также считаю, что все "предопределенные" свойства объектов являются не перечисляемыми. При этом я имею в виду исключительно нативные свойства, а не обязательно унаследованные или созданные вручную. Например, pop и push не будут перечисляться, но Array.prototype.indexOf будет, если он создан как полифил в старом браузере, который не поддерживает этот метод... это, конечно, можно избежать, если использовать Object.defineProperty, как в моем примере выше. Другой пример — это свойство length, которое также не будет перечисляться.

Вот общий пример: http://jsfiddle.net/aHJ3g/

Использование и определение Object.keys важно: "Возвращает массив собственных перечисляемых свойств данного объекта в том же порядке, что и при использовании цикла for-in (разница в том, что цикл for-in также перечисляет свойства в цепочке прототипов)." - из MDN - https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Object/keys

0

Одним из основных преимуществ, как я вижу, является то, что оно предотвращает загрязнение публичного пространства имен приватными свойствами объекта.

Предположим, вы создали и опубликовали мощную библиотеку под названием Cosmos. Пользователь открывает интерпретатор Node и создает новый экземпляр, вызывая конструктор:

var Cosmos = require('Cosmos');
var cosmos = new Cosmos('my empire');

Теперь пользователь просто набирает cosmos и нажимает Enter, чтобы увидеть, какой публичный API он поддерживает. Какой из двух вариантов вы хотите, чтобы пользователь увидел?

{ name: 'my empire',
  grow: [Function: grow],
  addStar: [Function: addStar],
  beautify: [Function: beautify],
  implode: [Function: implode],
  destroy: [Function: destroy] }

ИЛИ

{ _age: 25000,
  _size: 35000,
  _destroyed: false,
  name: 'my empire',
  _numStars: 200,
  _init: [Function: _init],
  grow: [Function: grow],
  _grow: [Function: _grow],
  addStar: [Function: addStar],
  _checkStatus: [Function: _checkStatus],
  beautify: [Function: beautify],
  implode: [Function: implode],
  destroy: [Function: destroy] }

Очевидно, предпочтительнее первый вариант, так как он менее загроможден и предоставляет пользователю четкое представление о доступном интерфейсе. Скрытие приватных свойств помогает сосредоточиться на публичных методах и свойствах, что повышает удобство использования вашей библиотеки.

0

Вы можете сделать свойство не перечисляемым, чтобы получить к нему доступ. Однако, когда вы используете цикл for...in по объекту, не перечисляемое свойство не будет итерироваться.

Например, обратите внимание на первый пункт.

Свойства, унаследованные от родителя, являются перечисляемыми (при условии, что они помечены как перечисляемые):

var x = {a: 1, b: 2}; // a и b являются перечисляемыми свойствами по умолчанию
x.propertyIsEnumerable("toString"); // вернет false, так как оно не помечено как перечисляемое
var y = Object.create(x);
y.c = 3;
for (p in y) console.log(p); // этот цикл выведет c, a и b, но не toString

Таким образом, свойство toString не будет отображаться в цикле, хотя к нему все еще можно получить доступ непосредственно через объект.

0

Вы можете использовать свойство array.length для получения длины массива, но если вы хотите избежать его использования в циклах или обходах, вы можете сохранить длину массива в отдельной переменной. Это позволит вам избежать повторного вычисления длины массива при каждой итерации:

let arr = [1, 2, 3, 4, 5];
let length = arr.length;

for (let i = 0; i < length; i++) {
    console.log(arr[i]);
}

Таким образом, вы один раз получаете длину массива, сохраняете ее в переменной length и используете эту переменную в цикле, что может немного повысить производительность и сделать код более чистым.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь