От автора: в этой статье рассматривается, какими возможностями обладает литерал объекта JavaScript, особенно в свете последних обновлений ECMAScript. В JavaScript мощная система создания объектов с использованием литерала. Новые функции, появившиеся в ES2015 (ES6), упрощают обработку объектов во всех современных браузерах (не IE) и Node.js.
Создание объектов на некоторых языках может быть дорогостоящим с точки зрения времени разработки и вычислительной мощности, когда class должен быть объявлен до того, как что-либо может быть достигнуто. В JavaScript легко создавать объекты «на лету». Например:
1 2 3 4 5 6 7 8 9 10 |
// ES5-compatible code var myObject = { prop1: 'hello', prop2: 'world', output: function() { console.log(this.prop1 + ' ' + this.prop2); } }; myObject.output(); // hello world |
Объекты одноразового использования широко используются. Примеры включают настройки конфигурации, определения модулей, параметры метода, возвращаемые значения из функций и т. д. ES2015 (ES6) добавил ряд функций для улучшения объектных литералов.
Инициализация объектов из переменных
Свойства объектов часто создаются из переменных с тем же именем. Например:
1 2 3 4 5 6 7 8 9 10 |
// ES5 code var a = 1, b = 2, c = 3; obj = { a: a, b: b, c: c }; // obj.a = 1, obj.b = 2, obj.c = 3 |
В ES6 нет необходимости в неприятных повторениях! …
1 2 3 4 5 6 7 8 9 10 |
// ES6 code const a = 1, b = 2, c = 3; obj = { a b c }; // obj.a = 1, obj.b = 2, obj.c = 3 |
Это может быть полезно для возвращаемых объектов при использовании раскрывающего шаблона модуля, который (эффективно) обозначает код пространства имен, чтобы избежать конфликтов имен. Например:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// ES6 code const lib = (() => { function sum(a, b) { return a + b; } function mult(a, b) { return a * b; } return { sum, mult }; }()); console.log( lib.sum(2, 3) ); // 5 console.log( lib.mult(2, 3) ); // 6 |
Возможно, вы видели, как он использовался в модулях ES6:
1 2 3 4 5 |
// lib.js function sum(a, b) { return a + b; } function mult(a, b) { return a * b; } export { sum, mult }; |
Сокращение определения метода объекта
Методы объектов в ES5 требуют оператора function. Например:
1 2 3 4 5 6 7 8 |
// ES5 code var lib = { sum: function(a, b) { return a + b; }, mult: function(a, b) { return a * b; } }; console.log( lib.sum(2, 3) ); // 5 console.log( lib.mult(2, 3) ); // 6 |
Это больше не требуется в ES6; он допускает следующий сокращенный синтаксис:
1 2 3 4 5 6 7 8 |
// ES6 code const lib = { sum(a, b) { return a + b; }, mult(a, b) { return a * b; } }; console.log( lib.sum(2, 3) ); // 5 console.log( lib.mult(2, 3) ); // 6 |
Здесь нельзя использовать синтаксис => функции, потому что для этого метода требуется имя. Тем не менее, вы можете использовать стрелочные функции, если вы называете каждый метод напрямую (например, ES5). Например:
1 2 3 4 5 6 7 8 |
// ES6 code const lib = { sum: (a, b) => a + b, mult: (a, b) => a * b }; console.log( lib.sum(2, 3) ); // 5 console.log( lib.mult(2, 3) ); // 6 |
Динамические ключи свойств
В ES5 невозможно было использовать переменную для имени ключа, хотя она могла быть добавлена после создания объекта. Например:
1 2 3 4 5 6 7 8 9 10 11 |
// ES5 code var key1 = 'one', obj = { two: 2, three: 3 }; obj[key1] = 1; // obj.one = 1, obj.two = 2, obj.three = 3 |
Ключи объектов можно динамически назначать в ES6, помещая выражение в [ квадратные скобки ]. Например:
1 2 3 4 5 6 7 8 9 10 |
// ES6 code const key1 = 'one', obj = { [key1]: 1, two: 2, three: 3 }; // obj.one = 1, obj.two = 2, obj.three = 3 |
Любое выражение может использоваться для создания ключа. Например:
1 2 3 4 5 6 7 8 |
// ES6 code const i = 1, obj = { ['i' + i]: i }; console.log(obj.i1); // 1 |
Динамический ключ может использоваться как для методов, так и для свойств. Например:
1 2 3 4 5 6 7 8 |
// ES6 code const i = 2, obj = { ['mult' + i]: x => x * i }; console.log( obj.mult2(5) ); // 10 |
Нужно ли создавать динамические свойства и методы — другое дело. Код может быть трудночитаемым, и может быть предпочтительнее создавать фабрики объектов или классы.
Деструктуризация (переменные из свойств объекта)
Часто бывает необходимо извлечь значение свойства из объекта в другую переменную. Это должно быть явно объявлено в ES5. Например:
1 2 3 4 5 6 7 8 9 10 11 |
// ES5 code var myObject = { one: 'a', two: 'b', three: 'c' }; var one = myObject.one, // 'a' two = myObject.two, // 'b' three = myObject.three; // 'c' |
ES6 поддерживает деструктуризацию: вы можете создать переменную с тем же именем, что и свойство эквивалентного объекта. Например:
1 2 3 4 5 6 7 8 9 |
// ES6 code const myObject = { one: 'a', two: 'b', three: 'c' }; const { one, two, three } = myObject; // one = 'a', two = 'b', three = 'c' |
Также возможно присвоить свойства переменным с любым именем, используя нотацию { propertyName: newVariable }. Например:
1 2 3 4 5 6 7 8 9 |
// ES6 code const myObject = { one: 'a', two: 'b', three: 'c' }; const { one: first, two: second, three: third } = myObject; // first = 'a', second = 'b', third = 'c' |
Более сложные объекты с вложенными массивами и под-объектами также могут ссылаться на деструктурирующие назначения. Например:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// ES6 code const meta = { title: 'Enhanced Object Literals', pageinfo: { url: '//www.sitepoint.com/', description: 'How to use object literals in ES2015 (ES6).', keywords: 'javascript, object, literal' } }; const { title : doc, pageinfo: { keywords: topic } } = meta; /* doc = 'Enhanced Object Literals' topic = 'javascript, object, literal' */ |
Это изначально кажется сложным, но помните, что во всех назначениях деструктуризации:
левая часть присваивания — источник деструктуризации — массив или объект, который содержит извлеченные данные
правая часть присваивания — цель деструктуризации — шаблон, который определяет назначаемую переменную.
Существует ряд предостережений. Вы не можете запустить инструкцию с фигурной скобкой, потому что она выглядит как блок кода. Например:
1 |
{ a, b, c } = myObject; // FAILS |
Вы должны либо объявить переменные — например:
1 |
const { a, b, c } = myObject; // WORKS |
или использовать круглые скобки, если переменные уже объявлены — например:
1 2 |
let a, b, c; ({ a, b, c } = myObject); // WORKS |
Поэтому вы должны быть осторожны, чтобы не смешивать объявленные и необъявленные переменные. Существует множество ситуаций, когда полезно деструктуризация объекта.
Параметры функции по умолчанию
Часто проще передать один объект функции, чем использовать длинный список аргументов. Например:
1 2 3 4 5 6 7 |
prettyPrint( { title: 'Enhanced Object Literals', publisher: { name: 'SitePoint', url: '//www.sitepoint.com/' } } ); |
В ES5 необходимо проанализировать объект, чтобы установить соответствующие значения по умолчанию. Например:
1 2 3 4 5 6 7 8 9 10 11 |
// ES5 assign defaults function prettyPrint(param) { param = param || {}; var pubTitle = param.title || 'No title', pubName = (param.publisher && param.publisher.name) || 'No publisher'; return pubTitle + ', ' + pubName; } |
В ES6 мы можем присвоить значение по умолчанию любому параметру. Например:
1 2 |
// ES6 default value function prettyPrint(param = {}) { ... } |
Затем мы можем использовать деструктуризацию для извлечения значений и при необходимости назначать значения по умолчанию:
1 2 3 4 5 6 7 8 9 10 11 |
// ES6 destructured default value function prettyPrint( { title: pubTitle = 'No title', publisher: { name: pubName = 'No publisher' } } = {} ) { return `${pubTitle}, ${pubName}`; } |
Считаете ли вы, что этот код легче читать, это другое дело!
Парсинг возвращаемых объектов
Функции могут возвращать только одно значение, но это может быть объект с сотнями свойств и / или методов. В ES5 необходимо получить возвращенный объект, а затем извлечь значения соответственно. Например:
1 2 3 4 5 6 |
// ES5 code var obj = getObject(), one = obj.one, two = obj.two, three = obj.three; |
Деструктуризация ES6 упрощает этот процесс, и нет необходимости сохранять объект как переменную:
1 2 |
// ES6 code const { one, two, three } = getObject(); |
Возможно, вы видели аналогичные назначения в коде Node.js. Например, если вам нужны только методы File System ( fs ) readFile и writeFile , вы можете ссылаться на них напрямую. Например:
1 2 3 4 5 6 7 8 9 10 |
// ES6 Node.js const { readFile, writeFile } = require('fs'); readFile('file.txt', (err, data) => { console.log(err || data); }); writeFile('new.txt', 'new content', err => { console.log(err || 'file written'); }); |
ES2018 (ES9) Свойства Rest/Spread
В ES2015 параметр rest и spread оператор ( … ) применяется только к массивам. ES2018 обеспечивает аналогичную функцию rest/spread для объектов. Основной пример:
1 2 3 4 5 6 7 8 9 |
const myObject = { a: 1, b: 2, c: 3 }; const { a, ...x } = myObject; // a = 1 // x = { b: 2, c: 3 } |
Вы можете использовать эту технику для передачи значений функции:
1 2 3 4 5 6 7 8 9 10 |
restParam({ a: 1, b: 2, c: 3 }); function restParam({ a, ...x }) { // a = 1 // x = { b: 2, c: 3 } } |
Вы можете использовать только одно свойство rest в конце объявления. Кроме того, он работает только на верхнем уровне каждого объекта, а не на под-объектах.
Оператор распространения может использоваться в других объектах. Например:
1 2 3 4 5 |
const obj1 = { a: 1, b: 2, c: 3 }, obj2 = { ...obj1, z: 26 }; // obj2 is { a: 1, b: 2, c: 3, z: 26 } |
Вы можете использовать оператор spread для клонирования объектов ( obj2 = { …obj1 }; ), но имейте в obj2 = { …obj1 };, что вы получаете только мелкие копии. Если свойство содержит другой объект, клон будет ссылаться на тот же объект.
ES2018 (ES9) поддержка свойств rest/spread неоднозначна, но доступна в Chrome, Firefox и Node.js 8.6+.
Объектные литералы всегда были полезны. Новые функции, введенные в ES2015, принципиально не изменили работу JavaScript, но они сохраняют усилия по набору текста и приводят к более четкому и более сжатому коду.
Автор: Craig Buckler
Источник: //www.sitepoint.com/
Редакция: Команда webformyself.