Как обрабатывать вложенные параметры по умолчанию с деструктурированием объекта?

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


1 - Деструктуризация плоского объекта с параметрами по умолчанию

Разрушить этот объект легко:

let obj = {
  foo: 'Foo',
  bar: 'Bar'
};

С {foo = 'Foo', bar = 'Bar'} = {} в сигнатуре функции объект будет создан, если при вызове функции не будет передан аргумент. Если объект передан, но некоторые ссылочные свойства имеют значение undefined, они будут заменены значениями по умолчанию. Этот код работает нормально:

function fn1({foo = 'Foo', bar = 'Bar'} = {}) {
  console.log(foo, bar);
}

// OK
fn1(); // Foo Bar
fn1({foo: 'Quux'}); // Quux Bar
fn1({bar: 'Quux'}); // Foo Quux
fn1({foo: 'Quux', bar: 'Quux'}); // Quux Quux

2 - Деструктуризация вложенных объектов с мелкими параметрами по умолчанию

Разрушить этот объект сложнее:

let obj = {
  foo: 'Foo',
  bar: {
    quux: 'Quux',
    corge: 'Corge'
  }
};

{foo = 'Foo', bar = 'Bar'} = {} больше не подходит, но теперь мы можем использовать {foo = 'Foo', bar = {quux: 'Quux', corge: 'Corge'}} = {} в сигнатуре функции. Опять же, если при вызове функции не задан аргумент, создается объект и извлекаются основные свойства (foo и bar). Если объект передан, только неопределенные свойства (foo или bar) будут деструктурированы со значениями по умолчанию.

Проблема в том, что свойства объекта bar (quux и corge) не являются частью "деструктуризации верхнего уровня". Это означает, что quux или corge будут равны undefined, если они не установлены явно при передаче bar в качестве аргумента:

function fn2({foo = 'Foo', bar = {quux: 'Quux', corge: 'Corge'}} = {}) {
  console.log(foo, bar.quux, bar.corge);
}

// OK
fn2(); // Foo Quux Corge
fn2({foo: 'Quux'}); // Quux Quux Corge

// Oops!
fn2({bar: {quux: 'Baz'}}); // Foo Baz undefined
fn2({foo: 'Quux', bar: {corge: 'Baz'}}); // Quux undefined Baz

3 - Деструктуризация вложенных объектов с глубокими параметрами по умолчанию

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

function fn3({foo = 'Foo', bar = ({quux = 'Quux', corge = 'Corge'} = {})} = {}) {
  console.log(foo, bar.quux, bar.corge);
}

// Oops!
fn3(); // Foo undefined undefined
fn3({foo: 'Quux'}); // Quux undefined undefined
fn3({bar: {quux: 'Baz'}}); // Foo Baz undefined
fn3({foo: 'Quux', bar: {corge: 'Baz'}}); // Quux undefined Baz


Знаете ли вы, разрешена ли такая функция в ES6. Если да, то как я могу это реализовать?


person Badacadabra    schedule 10.05.2017    source источник
comment
Для этого следует использовать прототипирование. Это не только упростит построение, но и улучшит память.   -  person Travis J    schedule 10.05.2017
comment
Честно говоря, я бы не стал использовать это в повседневном кодировании, потому что это трудно читать и, вероятно, интенсивно использовать память. Это просто эксперимент с ES6. Я пытаюсь проверить его пределы. :)   -  person Badacadabra    schedule 10.05.2017
comment
И как совместить это с псевдонимами?   -  person dude    schedule 07.06.2018


Ответы (3)


Общий шаблон для деструктурирования свойств объекта:

{ … , propertyName: target = defaultInitialiser, … }

(когда имя свойства точно совпадает с идентификатором целевой переменной, мы можем их соединить).

Но target предназначен не только для переменных, это может быть любая цель присваивания, включая вложенные выражения деструктурирования. Итак, для вашего случая (3) вы хотите использовать точно такой же подход, как и в (1) на верхнем уровне параметра - по умолчанию инициализируйте свойство пустым объектом и деструктурируйте его части:

function fn3({foo = 'Foo', bar: {quux = 'Quux', corge = 'Corge'} = {}} = {}) {
  console.log(foo, quux, corge);
}

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

function fn3({foo = 'Foo', bar, bar: {quux = 'Quux', corge = 'Corge'} = {}} = {}) {
  console.log(foo, bar, quux, corge);
}
person Bergi    schedule 10.05.2017
comment
ХОРОШО. Мой вывод состоит в том, что мы не можем деструктурировать bar и его свойства со значениями по умолчанию. Лучшее, что мы можем сделать, это деструктурировать bar части, поэтому в ES6 нет каскадной деструктуризации... Спасибо за ваш ответ. :) - person Badacadabra; 11.05.2017
comment
@Badacadabra На самом деле это вполне возможно. Но обычно это будет намного читабельнее, если вы просто поместите несколько операторов деструктурирования в тело функции. - person Bergi; 11.05.2017
comment
Интересное редактирование... Проблема, которую я вижу в этом предложении, заключается в том, что вы получите undefined вместо bar со следующими вызовами: fn3() или fn3({foo: 'Quux'}). Вы также получите частичный bar со следующими вызовами: fn3({bar: {quux: 'Baz'}}) или {foo: 'Quux', bar: {corge: 'Baz'}}. В любом случае, я согласен: этот код тяжело читать. - person Badacadabra; 11.05.2017
comment
Да, конечно, вы получите фактическое значение для bar. Если вам нужен объект, состоящий из quux и corge со значениями по умолчанию, вам нужно создать его явно: const bar = {quux, corge}. Деструктуризация была создана не для этого. - person Bergi; 11.05.2017
comment
Как я уже говорил в предыдущем комментарии, это по большей части эксперимент с ES6... Я бы не стал использовать такую ​​сложную штуку в стандартном проекте. Деструктуризация — мощная функция, но не так много вариантов использования нечитаемых вложенных параметров по умолчанию, как в этом вопросе. Но я думаю, что теперь я лучше понимаю ограничения, и это важно. :) - person Badacadabra; 11.05.2017

У меня есть кое-что попроще. У него есть недостатки. Но сначала товары:

function doit( arg1 = 'one', hash = { prop1: 'two', prop2: 'three' }, { prop1, prop2 } = hash ) {
    console.log(`arg1`, arg1)
    console.log(`prop1`, prop1)
    console.log(`prop2`, prop2)
    console.log(`hash`, hash)
}

Что это дает?

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

Как это работает?

Параметры по умолчанию ES6 могут относиться к другие параметры, например:

function math( x = 1, y = x ) { ... }
// x = 1
// y = 1

Таким образом, несмотря на то, что пример функции рассчитан на прием двух аргументов (arg1 и hash), сигнатура формально объявляется с тремя аргументами. Третий аргумент — это своего рода вымышленный или временный аргумент, который существует исключительно с целью деструктурирования hash. Это логический эквивалент этого:

function doit( arg1 = 'one', hash = { prop1: 'two', prop2: 'three' } ) {
    let { prop1, prop2 } = hash
    ...
}

Преимущество этого шаблона в том, что подпись полностью самодокументируема. К сожалению, в JS очень часто можно увидеть сигнатуры, объявленные следующим образом:

function terminateEmployee( employeeId, options ) {
    // what properties does `options` accept??
}

Чтобы ответить на этот вопрос, вам нужно выполнить поиск по всем нижестоящим кодовым путям и собрать все случаи использования options. Иногда этот кодовый путь очень длинный; если вам не повезло работать в экосистеме, основанной на микросервисах, этот путь кода может охватывать две или более дополнительные кодовые базы на других языках (правдивая история).

Да, мы можем попросить разработчиков написать документацию, но на этот счет YMMV.

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

Недостатком является то, что функция выглядит так, как будто она принимает три аргумента — и на самом деле принимает три. Таким образом, разработчики, которые не знают, что происходит, могут быть введены в заблуждение. И если вызывающий объект передает три аргумента, третий аргумент переопределяет второй аргумент.

person Tom    schedule 06.03.2019

как насчет

function fn3({foo = 'Foo', bar={} } = {}) {
   const {quux = 'Quux', corge = 'Corge'} = bar;
    console.log(foo, quux, corge);
}


fn3(); // Foo Quux Corge
fn3({foo: 'Quux'}); // Quux Quux Corge
fn3({bar: {quux: 'Baz'}}); // Foo Baz Corge
fn3({foo: 'Quux', bar: {corge: 'Baz'}}); // Quux Quux Baz

person Patrik Neunteufel    schedule 16.07.2020