- Enumerability and ownership of properties
- Доступ к свойствам по их перечисляемости/принадлежности
- Определимость свойств
- Смотрите также
- Found a content problem with this page?
- Using Enums (Enumerations) In Javascript
- Defining Enums as Object Keys#
- Enums with Symbols#
- Making Enum Objects Immutable#
- Enums with Classes#
- Listing All Possible Enum Values#
- When to Use Enums in Javascript?#
Enumerability and ownership of properties
В языке JavaScript свойства объектов могут быть перечисляемыми или неперечисляемыми (встречается вариант перевода: счётные или несчётные). Если внутреннему флагу [[Enumerable]] свойства присвоить значение true, то данное свойство становится перечисляемым. Это происходит по умолчанию для свойств, созданных простым присваиванием или через инициализацию свойств (свойства, определённые через Object.defineProperty получают по умолчанию значение флага [[Enumerable]] равным false). Перечисляемые свойства участвуют в итерации в цикле for. in, если только имя свойства не Символ. Принадлежность свойства определяется тем, принадлежит ли оно непосредственно объекту или получено из цепочки прототипов. Также можно получить весь список свойств объекта. Ниже, в таблице, указаны возможные способы нахождения, получения и итерации свойств объектов. Некоторые из них нельзя использовать без дополнительного кода, примеры которого приведены после таблицы.
Доступ к свойствам по их перечисляемости/принадлежности
Хотим заметить, что данный алгоритм эффективен не для всех классов.
- Определение свойства: SimplePropertyRetriever.theGetMethodYouWant(obj).indexOf(prop) > -1
- Итерация: SimplePropertyRetriever.theGetMethodYouWant(obj).forEach(function (value, prop) <>); (or use filter() , map() , etc.)
var SimplePropertyRetriever = getOwnEnumerables: function (obj) return this._getPropertyNames(obj, true, false, this._enumerable); // Или можно использовать for..in, отфильтрованный по hasOwnProperty или проще: return Object.keys(obj); >, getOwnNonenumerables: function (obj) return this._getPropertyNames(obj, true, false, this._notEnumerable); >, getOwnEnumerablesAndNonenumerables: function (obj) return this._getPropertyNames(obj, true, false, this._enumerableAndNotEnumerable); // Или можно использовать: return Object.getOwnPropertyNames(obj); >, getPrototypeEnumerables: function (obj) return this._getPropertyNames(obj, false, true, this._enumerable); >, getPrototypeNonenumerables: function (obj) return this._getPropertyNames(obj, false, true, this._notEnumerable); >, getPrototypeEnumerablesAndNonenumerables: function (obj) return this._getPropertyNames(obj, false, true, this._enumerableAndNotEnumerable); >, getOwnAndPrototypeEnumerables: function (obj) return this._getPropertyNames(obj, true, true, this._enumerable); // Или можно использовать for..in >, getOwnAndPrototypeNonenumerables: function (obj) return this._getPropertyNames(obj, true, true, this._notEnumerable); >, getOwnAndPrototypeEnumerablesAndNonenumerables: function (obj) return this._getPropertyNames(obj, true, true, this._enumerableAndNotEnumerable); >, // Private static property checker callbacks _enumerable : function (obj, prop) return obj.propertyIsEnumerable(prop); >, _notEnumerable : function (obj, prop) return !obj.propertyIsEnumerable(prop); >, _enumerableAndNotEnumerable : function (obj, prop) return true; >, // По мотивам http://stackoverflow.com/a/8024294/271577 _getPropertyNames : function getAllPropertyNames(obj, iterateSelfBool, iteratePrototypeBool, includePropCb) var props = []; do if (iterateSelfBool) Object.getOwnPropertyNames(obj).forEach(function (prop) if (props.indexOf(prop) === -1 && includePropCb(obj, prop)) props.push(prop); > >); > if (!iteratePrototypeBool) break; > iterateSelfBool = true; > while (obj = Object.getPrototypeOf(obj)); return props; > >;
Определимость свойств
in | for..in | hasOwnProperty | propertyIsEnumerable | in Object.keys | in Object.getOwnPropertyNames | |
---|---|---|---|---|---|---|
Перечисляемые | true | true | true | true | true | true |
Неперечисляемые | true | false | true | false | false | true |
Унаследованные Перечисляемые | true | true | false | false | false | false |
Унаследованные Неперечисляемые | true | false | false | false | false | false |
Смотрите также
Found a content problem with this page?
Using Enums (Enumerations) In Javascript
This post will explain how to implement and use enumerations (or enum types) in Javascript.
Enums are types that contain a limited number of fixed values, as opposed to types like Number or String which can have a wide range of values.
This is useful for many situations: For example, when describing seasons in temperate climates, you’ll want to limit the options to certain possible values: Summer , Winter , Spring and Autumn .
Let’s look at different ways of implementing this type of data:
Defining Enums as Object Keys#
For a basic implementation of enums, we can define an object to encapsulate the enum type, and assign a key for each enum value.
For example, we can represent seasons as on object with each season as the key, and a representative string as the value:
const Seasons = Summer: "summer", Autumn: "autumn", Winter: "winter", Spring: "spring" >
We can also do this by using numbers as values:
const Seasons = Summer: 0, Autumn: 1, Winter: 2, Spring: 3 >
It’s easy to make mistakes in your code. A developer can make the mistake of using integers outside the range of the ones defined, or make a spelling mistake when using strings.
const Seasons = Summer: "summer", Autumn: "autumn", Winter: "winter", Spring: "spring" > const mySeason = "summr" // this would be false because of the spelling mistake in `mySeason` console.log(mySeason === Seasons.Summer)
Definitions from unrelated enums can overlap and cause conflicts:
const Seasons = Summer: 0, Autumn: 1, Winter: 2, Spring: 3 > const Fruits = Apple: 1, Orange: 1 > // Ideally, this should never be true! console.log(Seasons.Summer === Fruits.Apple)
Enums with Symbols#
Symbols let us define values that are guaranteed not to collide with one another.
const Summer1 = Symbol("summer") const Summer2 = Symbol("summer") // Even though they have the same apparent value // Summer1 and Summer2 don't equate console.log(Summer1 === Summer2) // false console.log(Summer1)
We can define our enums using Symbols to ensure that they are not duplicated:
const Seasons = Summer: Symbol("summer"), Autumn: Symbol("autumn"), Winter: Symbol("winter"), Spring: Symbol("spring") > let season = Seasons.Spring switch (season) case Seasons.Summer: console.log('the season is summer') break; case Seasons.Winter: console.log('the season is winter') break; case Seasons.Spring: console.log('the season is spring') break; case Seasons.Autumn: console.log('the season is autumn') break; default: console.log('season not defined') >
Using Symbols ensures that the only way we can assign an enum value is by using the values that we defined initially.
Please note that Symbols cannot be serialized into JSON, so if you plan on converting an object containing this enum into a JSON string, you should consider the previous method of using object keys instead
Making Enum Objects Immutable#
In the previous examples, it’s possible to change the value of an enum by modifying the enum object. For example, we can change the Seasons object like so:
const Seasons = Summer: Symbol("summer"), Autumn: Symbol("autumn"), Winter: Symbol("winter"), Spring: Symbol("spring") > Seasons.Winter = "winter" // this will overwrite the `Symbol` with a string
For larger codebases, these kind of errors may be introduced unintentionally. We can use Object.freeze to prevent these kind of changes:
const Seasons = Object.freeze( Summer: Symbol("summer"), Autumn: Symbol("autumn"), Winter: Symbol("winter"), Spring: Symbol("spring") >) Seasons.Winter = "winter" // this won't change the `Seasons` object because its been frozen
Enums with Classes#
To make our code more semantically correct, we can create a class to hold groups of enums.
For example, our seasons should have a way for us to identify that they all belong to a similar classification.
Let’s see how we can use classes and objects to create distinct enum groups:
// Season enums can be grouped as static members of a class class Season // Create new instances of the same class as static attributes static Summer = new Season("summer") static Autumn = new Season("autumn") static Winter = new Season("winter") static Spring = new Season("spring") constructor(name) this.name = name > > // Now we can access enums using namespaced assignments // this makes it semantically clear that "Summer" is a "Season" let season = Season.Summer // We can verify whether a particular variable is a Season enum console.log(season instanceof Season) // true console.log(Symbol('something') instanceof Season) //false // We can explicitly check the type based on each enums class console.log(season.constructor.name) // 'Season'
Listing All Possible Enum Values#
If we used the class-based approach above, we can loop through the keys of the Season class to obtain all the enum values under the same group:
Object.keys(Season).forEach(season => console.log("season:", season)) // season: Summer // season: Autumn // season: Winter // season: Spring
We can do the same with the object-based approach as well:
const Seasons = Object.freeze( Summer: Symbol("summer"), Autumn: Symbol("autumn"), Winter: Symbol("winter"), Spring: Symbol("spring") >) Object.keys(Seasons).forEach(season => console.log("season:", season)) // season: Summer // season: Autumn // season: Winter // season: Spring
When to Use Enums in Javascript?#
In general, enums are helpful if there are a definite number of fixed values for any one variable.
For example, the crypto standard library for Node.js has a list of supported algorithms, that can be considered an enum group.
Using enums in Javascript correctly will lead to better code that is more stable, easier to read and less error prone.