Инициализаторы объектов и необычные свободы, которые они предоставляют программисту
Давайте начнем с теста: сможете ли вы сказать из этого определения объекта, сколько и каковы свойства myObj?
var myObj = { '': 1, "": 2, [[]]: 3, ['']: 4, 1: 5, 1.0: 6, '1': 7, [1]: 8, [[1]]:9, _: 10 };
Как сказано на соответствующей странице MDN:
Инициализатор объекта — это разделенный запятыми список из нуля или более пар имен свойств и связанных значений объекта, заключенных в фигурные скобки (
{}
).
На самом деле это немного преуменьшение, как показано чуть ниже в списке возможных форм, которые может принимать пара свойство-значение:
o = { a: "foo", b: 42, c: {}, 1: "number literal property", "foo:bar": "string literal property", shorthandProperty, method(parameters) { // … }, get property() {}, set property(value) {}, [expression]: "computed property", __proto__: prototype, ...spreadProperty, };
Если вы сравните этот список возможных форматов с нашим примером, то обнаружите, что мы использовали в качестве свойств
— обычный идентификатор: _(да, символ _ является допустимым идентификатором в JS)
- два числа: 1 и 1.0
- три строковых литерала: '', "", '1'
— три вычисляемых имени свойства: [''], [[]], [1], [[1]]
Итак, каков ответ? Сколько и какие свойства есть у myObj?
Ответ на викторину
Я не думаю, что легко быть уверенным в правильном ответе, не попробовав его вживую. На самом деле, конкретный способ, которым JavaScript интерпретирует «имена» свойств объекта, не сразу очевиден, в случаях, созданных так, чтобы быть странными, как этот.
Это содержимое объекта myObj, определенного выше:
{ "1": 9, "": 4, "_": 10 } // verify console.log(myObj[1]); // 9 console.log(myObj['']); // 4 console.log(myObj._); // 10
Последнее свойство (_) самое простое: это обычный идентификатор (т. е. строка, допустимая в качестве имени переменной JavaScript), точно так же, как и такие имена, как $, $$. , ___, $_ù_$ и т. д.
Свойство "1" несколько более удивительно: оно является результатом следующих пяти пунктов в определении:
1: 5, 1.0: 6, '1': 7, [1]: 8, [[1]]:9,
Для JavaScript все эти значения приводят к строке «1», поскольку для всех них неявно выполняется .toString(). Обратите внимание, что последний определяет вычисляемое свойство как массив, содержащий число 1, а метод по умолчанию .toString() для этого массива просто возвращает строку «1».
Поскольку все 5 определений создают одно и то же имя свойства, JavaScript применяет их по порядку, и, таким образом, в результате свойство 1 имеет последнее связанное с ним значение, т. е. 9.
Свойство "" (пустая строка), пожалуй, самое странное, так как можно было бы подумать, что имя свойства должно быть чем-то "видимым", но это не так. Определение присваивает значение 4 из-за эквивалентности следующих четырех элементов в инициализаторе:
'': 1, "": 2, [[]]: 3, ['']: 4,
Причина, по которой все четыре строки определяют одно и то же свойство, та же, что и раньше, а именно неявное применение метода .toString() для создания имени свойства.
Дополнительные странности
Теперь, когда мы изучили основы, другие психически больные комбинации становятся почти понятными:
var myObj = { [Date]: Date, [new Date()]: new Date(), [{}]: [{}], null: null, undefined: undefined, ':': ':', [Math.PI]: Math.PI, ààà: 'ààà' }; // List property-value couples for (p in myObj) console.log('"'+p+'":', myObj[p]); /* Output: "function Date() { [native code] }": ƒ Date() { [native code] } "Tue May 16 2023 10:46:35 GMT+0200 (Ora legale dell’Europa centrale)": Tue May 16 2023 10:46:35 GMT+0200 (Ora legale dell’Europa centrale) "[object Object]": [{…}] "null": null "undefined": undefined ":": : "3.141592653589793": 3.141592653589793 "ààà": ààà */
Обратите внимание на то, как JavaScript создает имя свойства в виде строки для функций (дата), общих объектов ([{}]) и экземпляров классов благодаря специальному методу .toString() (новая дата). Обратите также внимание на то, что свойство можно назвать любым символом (:), буквами с диакритическими знаками (ààà) и даже ключевыми словами, которые нельзя использовать в качестве идентификаторов (null). , не определено).
Может ли все это быть полезным?
Маловероятно, что в обычном проекте фронтенд-разработки вы столкнетесь с определением объектов с такими нестандартными именованными свойствами. Но пока мы на этом, я предлагаю два варианта использования.
Случай 1: Статистика использования пунктуации
С помощью нескольких строк кода можно разобрать текст и получить подсчет количества вхождений каждого из знаков препинания, где результат сохраняется в объекте JavaScript, где каждый знак препинания называет свойство объекта:
// Read the text of Ulysses by Joice fetch('https://www.gutenberg.org/files/4300/4300-0.txt') .then( response => response.text() ) .then( data => { // Parse the punctuation symbols and count them let result = {}; data.match(/[-,.;:!?"']/g).forEach( char => { result[char] = (result[char]??0) +1; } ); console.log(JSON.stringify(result)); });
Результат:
{ ",": 16521, ".": 22487, "-": 311, ":": 2584, "!": 1575, "?": 2233, ";": 32, "\"": 22, "'": 7 }
Сравнивая этот результат с таким же в Гамлете У. Шекспира, кажется, что точка с запятой была в то время более модной:
{ ",": 3361, ".": 3483, "-": 183, ":": 108, "?": 448, "!": 201, ";": 387, "\"": 22, "'": 7 }
Случай 2: конфигурации по умолчанию
Мы также можем воспользоваться специальными именами свойств, чтобы указать, по крайней мере, в некоторых ситуациях, значения по умолчанию в объектах конфигурации. Например, в следующем фрагменте кода мы используем два свойства с именами undefined и пустая строка, чтобы покрыть случаи, когда параметр конфигурации отсутствует:
var currency = { us: 'USD', fr: 'EUR', it: 'EUR', undefined: 'unknown (missing)', '': 'unknown (empty)' }; let trans1 = {fromCountry: 'fr'}; let trans2 = {fromCountry: 'us', toCountry: ''}; console.log(currency[trans1.fromCountry]); // EUR console.log(currency[trans1.toCountry]); // unknown (missing) console.log(currency[trans2.toCountry]); // unknown (empty)
Выводы
Мы показали особый аспект синтаксиса JavaScript, связанный с тем, как свойства объекта могут принимать имена, которые не являются реальными именами, что делает объект чем-то более похожим на хэш. table на других языках программирования.
Эта свобода именования не кажется особенно полезной функцией по сравнению с более приземленным стандартным правилом именования свойств объекта, но могут быть некоторые интересные приложения, и я прошу тех, у кого они есть, указать мне на них в комментарии.
Спасибо за внимание.