Каковы преимущества создания неперечислимых свойств?

Перечислимость — это один из трех атрибутов свойства: возможность записи, перечисляемость и возможность настройки. Мои вопросы:

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

person Anshul    schedule 20.02.2013    source источник
comment
Я думаю, что основное преимущество заключается в том, чтобы сделать циклы for in безопасными — свойство не будет отображаться при повторении объекта. Может быть, я забыл, что делает перечисление...   -  person Ian    schedule 20.02.2013


Ответы (4)


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

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

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

Итак, вместо чего-то вроде:

Object.prototype.myMethod = function () {
    alert("Ahh");
};

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

Object.defineProperty(Object.prototype, 'myMethod', {
    value: function () {
        alert("Ahh");
    },
    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/en-US/docs/JavaScript/Reference/Global_Objects/Object/keys

person Ian    schedule 20.02.2013

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

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

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

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

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

OR

{ _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] }
person Gary Chang    schedule 28.07.2013
comment
неперечислимые свойства по-прежнему являются общедоступными и загрязняют пространство имен. Если вы хотите частную собственность, вам нужно закрытие. - person Matthew; 26.01.2016
comment
Я проголосовал и за ответ, и за критический комментарий. Критика комментария кажется мне обоснованной, поэтому, возможно, использование ответчиком public и private может быть не оптимальным. Тем не менее, я думаю, что смысл ответа по-прежнему полезен (по крайней мере, для меня), потому что он показывает, как перечисление может помочь пользователю библиотеки более легко увидеть, что автор библиотеки хотел, чтобы пользователь библиотеки мог легко использовать (например, пожалуйста, используйте grow а не _grow, даже если оба технически все еще общедоступны). - person Andrew Willems; 08.03.2016

Нет реального практического применения неперечислимых свойств.

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

Задний план

Для тех, кто не знает, что такое неперечислимое свойство, взгляните на ссылки ниже:

Вы действительно можете перечислить их

Это странно, но перечисление указанных «неперечислимых» свойств на самом деле очень просто:

// start with some enumerable properties
const foo = {
  a: 1,
  b: "yes",
  c: function () {}
}

// then add a couple of non-enumerable ones
Object.defineProperty(foo, "d", { value: "hiding here", isEnumerable: false });
Object.defineProperty(foo, "e", { value: 42, isEnumerable: false });

const enumerableProperties = Object.keys(foo).join(", ");
console.info("Enumerables: " + enumerableProperties);
// Enumerables: a, b, c

const ownPropertyNames = Object.getOwnPropertyNames(foo).join(", ");
console.info("Enumerating also the non-enumerables: " + ownPropertyNames);
// Enumerating also the non-enumerables: a, b, c, d, e

Когда они говорят, что вы не можете перечислить их, они имеют в виду конкретно Object.keys() и цикл for..in, а также тот факт, что они возвращают только перечисляемые свойства. Это не относится к getOwnPropertyNames(). .

Не используйте это

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

Оба существующих ответа говорят о двух очень конкретных случаях:

  1. это полезно, когда вам приходится возиться с прототипом какого-либо стороннего объекта, чтобы добавить какой-либо метод полифилла, не нарушая существующий код в конкретном случае, когда код не защищает себя с помощью hasOwnProperty() или Object.keys(). Если это ваш случай, вам приходится поддерживать очень старый код (т. е. устаревший код, который не соответствует лучшим сегодняшним практикам), и мне жаль вас (хотя я знаю, что сегодня все еще поддерживается множество систем, которые попасть в тот случай, к сожалению);

  2. это полезно, когда вы разрабатываете общедоступную библиотеку и хотите, чтобы ваш общедоступный объект оставался чистым. Это очень конкретно, да? И я также предпочел бы не загрязнять код моей библиотеки несколькими defineProperty только для этого. Более того, этот ответ быстро устаревает, потому что теперь у нас есть частные поля. Наконец, в ответе также говорится, что это также будет поддерживать чистоту общедоступного пространства имен, но это не так; пространство имен загрязняется самим объектом, а не его свойствами.

Так что, если вы не находитесь в неудачном положении, когда вам приходится поддерживать старый код, забудьте об этом. Тебе это не нужно, поверь мне. Если вы оказываетесь в ситуации, когда вам нужно скрыть некоторые свойства при перечислении объекта, вы, безусловно, неправильно его моделируете. Просто поместите эти свойства в отдельный объект. Они не предназначены для того, чтобы жить вместе с другим имуществом, которое у вас есть. Это сделает ваш код чище и понятнее, а это самая важная вещь, к которой вы должны стремиться при написании кода.

Вот более подробная статья, подтверждающая идею о том, что неперечислимые свойства не играют существенной роли роль в наше время.

person Lucio Paiva    schedule 15.12.2019

  • Сделав свойство неперечислимым, вы все равно можете получить к нему доступ. Но когда вы применяете цикл for in к объекту, неперечислимое свойство не будет повторяться.
  • См. первый пункт
  • Унаследованные свойства являются перечислимыми (если они помечены как перечисляемые)

    var x = {a:1, b:2} // a and b are enumerable properties by default
    x.propertyIsEnumerable("toString") // returns false, because it is not marked as enumerable
    var y = Object.create(x);
    y.c = 3;
    for(p in y) console.log(p); // this loop will print c, a and b but not toString
    
person daniatic    schedule 20.02.2013
comment
Я считаю, что третий пункт неверен. Унаследованные свойства, безусловно, так же перечислимы, как и любые другие... var x = {a: 1}; var y = Object.create(x); y.b = 2; for (name in y) {console.log(name, y[name]);} дает b 2 и a 1 . - person Scott Sauyet; 20.02.2013
comment
Я исправил свой ответ. Простите мою ошибку! - person daniatic; 20.01.2020