Можно ли деструктурировать на существующий объект? (Javascript ES6)

137

Например, если у меня есть два объекта:

var foo = {
  x: "bar",
  y: "baz"
}

и

var oof = {}

и я хотел перенести значения x и y из foo в oof. Есть ли способ сделать это с помощью синтаксиса деструктуризации es6?

возможно что-то вроде:

oof{x,y} = foo
majorBummer
источник
10
Если вы хотите скопировать все свойстваObject.assign(oof, foo)
elclanrs
Здесь на MDN много разрушающих примеров , но я не вижу того, о котором вы спрашиваете.
jfriend00
Хм, я тоже не смог найти ...
majorBummer
См. Мой ответ ниже для двухстрочного решения безObject.assign
Zfalen

Ответы:

118

Уродливый и немного повторяющийся, вы можете сделать

({x: oof.x, y: oof.y} = foo);

который будет читать два значения fooобъекта и записывать их в соответствующие места на oofобъекте.

Лично я бы предпочел прочитать

oof.x = foo.x;
oof.y = foo.y;

или

['x', 'y'].forEach(prop => oof[prop] = foo[prop]);

хотя.

loganfsmyth
источник
2
Альтернатива ['x', 'y']. ForEach (prop => oof [prop] = foo [prop]); как я вижу, это единственный СУХОЙ (не повторяйтесь) до сих пор. «СУХОЙ» действительно был бы полезен в случае отображения нескольких ключей. Вам нужно будет обновить только одно место. Может я ошибаюсь. Спасибо, в любом случае!
Владимир Бразил
1
Третий пример - СУХОЙ, но в качестве ключей необходимо использовать строки, что обычно неявно делает javascript. Не уверен, что это меньше неприятностей, чем: const {x, y} = foo; const oof = {x, y};
Джефф Лоури
Может ли кто-нибудь показать мне в jsfiddle, где работает первое предложение? Здесь не работает Chrome: jsfiddle.net/7mh399ow
techguy2000,
@ techguy2000 Вы забыли точку с запятой после var oof = {};.
loganfsmyth
В первом коде не должно быть ({x: oof.x, y: oof.y} = foo)? Я думаю , что вы положили дополнительный п в OOF .
Sornii
38

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

Однако вы можете использовать Object.assign- если вам не нужны все собственные свойства, вы все равно можете сделать

var foo = …,
    oof = {};
{
    let {x, y} = foo;
    Object.assign(oof, {x, y})
}
Берги
источник
3
@loganfsmyth, ваш пример не работает для меня, по крайней мере, в моих тестах с узлом 4.2.2. с --harmony_destructuringвключенным. Я вижу «SyntaxError: неожиданный токен».
jpierson
@loganfsmyth, вы должны представить правильный ответ, так как это очень полезный комментарий, imo.
Sulliwane
@Sulliwane: Он сделал это (сейчас) . Берги, вероятно, было бы лучше обновить ответ, чтобы указать, что вы можете использовать выражения членов, как показано Логаном. (Неужели правда, что спецификация так сильно изменилась в период с апреля по июнь 2015 года?)
TJ Crowder
@TJCrowder Нет, я не думаю, что это изменилось, наверное, я имел в виду {oof.x, oof.y} = foo. Или, может быть, я действительно это пропустил.
Bergi
29

ИМО, это самый простой способ выполнить то, что вы ищете:

let { prop1, prop2, prop3 } = someObject;
let data = { prop1, prop2, prop3 };

  // data === { prop1: someObject.prop1, ... }

По сути, деструктурируйте в переменные, а затем используйте сокращение инициализатора для создания нового объекта. Нет необходимости вObject.assign

В любом случае, я думаю, что это наиболее читаемый способ. Таким образом, вы можете выбрать именно тот реквизит, someObjectкоторый вам нужен. Если у вас есть существующий объект, в который вы просто хотите объединить реквизиты, сделайте что-то вроде этого:

let { prop1, prop2, prop3 } = someObject;
let data = Object.assign(otherObject, { prop1, prop2, prop3 });
    // Makes a new copy, or...
Object.assign(otherObject, { prop1, prop2, prop3 });
    // Merges into otherObject

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

let { prop1, prop2, prop3 } = someObject;
let newObject = { prop1, prop2, prop3 };

// Merges your selected props into otherObject
Object.assign(otherObject, newObject);

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

РЕДАКТИРОВАТЬ: PS - Недавно я узнал, что вы можете использовать ультра-деструктуризацию на первом этапе, чтобы вытащить вложенные значения из сложных объектов! Например...

let { prop1, 
      prop2: { somethingDeeper }, 
      prop3: { 
         nested1: {
            nested2
         } 
      } = someObject;
let data = { prop1, somethingDeeper, nested2 };

Кроме того, вы можете использовать оператор распространения вместо Object.assign при создании нового объекта:

const { prop1, prop2, prop3 } = someObject;
let finalObject = {...otherObject, prop1, prop2, prop3 };

Или...

const { prop1, prop2, prop3 } = someObject;
const intermediateObject = { prop1, prop2, prop3 };
const finalObject = {...otherObject, ...intermediateObject };
Zfalen
источник
Мне нравится такой подход, прагматичный.
Джесси
2
Я так и делаю, но не очень сухо. Я думаю, что многим действительно нужна просто функция извлечения ключей.
jgmjgm
@jgmjgm Да, было бы неплохо, если бы он был встроен, но я думаю, это тоже не так сложно написать.
Zfalen
12

Кроме Object.assignесть объект распространения синтаксис , который является 2 -й этап предложение по ECMAScript.

var foo = {
  x: "bar",
  y: "baz"
}

var oof = { z: "z" }

oof =  {...oof, ...foo }

console.log(oof)

/* result 
{
  "x": "bar",
  "y": "baz",
  "z": "z"
}
*/

Но чтобы использовать эту функцию, вам нужно использовать stage-2или transform-object-rest-spreadплагин для babel. Вот демо на Babel сstage-2

GAFI
источник
9
Фактически это не устанавливает свойства существующего объекта, а скорее создает новый объект, содержащий все свойства обоих.
Мэтт Браун
8

Плагин BabelJS

Если вы используете BabelJS , теперь вы можете активировать мой плагин babel-plugin-transform-object-from-destructuring( см. Пакет npm для установки и использования ).

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

Пример объекта

let myObject = {
  test1: "stringTest1",
  test2: "stringTest2",
  test3: "stringTest3"
};
let { test1, test3 } = myObject,
  myTest = { test1, test3 };

можно записать как:

let myTest = { test1, test3 } = myObject;

Пример массива

let myArray = ["stringTest1", "stringTest2", "stringTest3"];
let [ test1, , test3 ] = myArray,
  myTest = [ test1, test3 ];

можно записать как:

let myTest = [ test1, , test3 ] = myArray;
Маттиас Гюнтер
источник
Именно этого я и хотел. Спасибо!
garrettmaring
let myTest = { test1, test3 } = myObject;не работает
Eatdoku
4

Вполне возможно. Только не в одном заявлении.

var foo = {
    x: "bar",
    y: "baz"
};
var oof = {};
({x: oof.x, y: oof.y} = foo); // {x: "bar", y: "baz"}

(Обратите внимание на круглые скобки вокруг утверждения.) Но имейте в виду, что удобочитаемость важнее, чем игра в код :).

Источник: http://exploringjs.com/es6/ch_destructuring.html#sec_assignment-targets

Хампус Альгрен
источник
3

Для этого можно просто использовать реструктуризацию:

const foo = {x:"a", y:"b"};
const {...oof} = foo; // {x:"a", y:"b"} 

Или объедините оба объекта, если oof имеет значения:

const foo = {x:"a", y:"b"};
let oof = {z:"c"}
oof = Object.assign({}, oof, foo)
CampSafari
источник
Я думаю, что этот первый случай - именно то, что я искал. Я попробовал это в хромированной консоли, и вроде работает. Ура! @campsafari
majorBummer
1
@majorBummer Ваш звонок в конце, но я бы рассмотрел подход, чтобы не отвечать на ваш вопрос, потому что они оба создают новые объекты, а не добавляют свойства к существующим объектам, как требует ваш заголовок.
loganfsmyth 03
Хороший момент @loganfsmyth. Думаю, меня просто взволновал метод, который использовал campSafari, потому что я не понимал, что это возможно.
majorBummer
1
Если вы не против создать новый объект, вы можете просто сделать это,oof = {...oof, ...foo}
Джошуа Коуди
2

Вы можете вернуть деструктурированный объект в стрелочной функции и использовать Object.assign (), чтобы присвоить его переменной.

const foo = {
  x: "bar",
  y: "baz"
}

const oof = Object.assign({}, () => ({ x, y } = foo));
Бен Коупленд
источник
1

DRY

var a = {a1:1, a2: 2, a3: 3};
var b = {b1:1, b2: 2, b3: 3};

const newVar = (() => ({a1, a2, b1, b2})).bind({...a, ...b});
const val = newVar();
console.log({...val});
// print: Object { a1: 1, a2: 2, b1: 1, b2: 2 }

или

console.log({...(() => ({a1, a2, b1, b2})).bind({...a, ...b})()});
user5733033
источник
1

Вы можете разрушить объект, назначив его непосредственно другому атрибуту объекта.

Рабочий пример:

let user = {};
[user.name, user.username] = "Stack Overflow".split(' ');
document.write(`
1st attr: ${user.name} <br /> 
2nd attr: ${user.username}`);

Вы можете работать с деструкцией, используя переменные с тем же именем атрибута объекта, который вы хотите поймать, таким образом вам не нужно:

let user = { name: 'Mike' }
let { name: name } = user;

Используйте этот способ:

let user = { name: 'Mike' }
let { name } = user;

Таким же образом вы можете установить новые значения для структур объектов, если они имеют одинаковое имя атрибута.

Посмотрите этот рабочий пример:

// The object to be destructed
let options = {
  title: "Menu",
  width: 100,
  height: 200
};

// Destructing
let {width: w, height: h, title} = options;

// Feedback
document.write(title + "<br />");  // Menu
document.write(w + "<br />");      // 100
document.write(h);                 // 200

RPichioli
источник
0

Это работает в Chrome 53.0.2785.89

let foo = {
  x: "bar",
  y: "baz"
};

let oof = {x, y} = foo;

console.log(`oof: ${JSON.stringify(oof)});

//prints
oof: {
  "x": "bar",
  "y": "baz"
}
user1577390
источник
7
К сожалению, похоже, что он oofпросто получает ссылку fooи обходит всю деструктуризацию (по крайней мере, в Chrome). oof === fooи let oof = { x } = fooвсе же возвращается{ x, y }
Theo.T
@ Theo.T хороший улов. это облом, мне придется найти другой способ
user1577390
Это стоит оставить, чтобы продемонстрировать, как JS может сбивать с толку.
jgmjgm
0

Я придумал такой метод:

exports.pick = function pick(src, props, dest={}) {
    return Object.keys(props).reduce((d,p) => {
        if(typeof props[p] === 'string') {
            d[props[p]] = src[p];
        } else if(props[p]) {
            d[p] = src[p];
        }
        return d;
    },dest);
};

Что вы можете использовать так:

let cbEvents = util.pick(this.props.events, {onFocus:1,onBlur:1,onCheck:'onChange'});
let wrapEvents = util.pick(this.props.events, {onMouseEnter:1,onMouseLeave:1});

то есть, вы можете выбрать, какие свойства вы хотите получить, и поместить их в новый объект. В отличие от них _.pickвы также можете переименовать их одновременно.

Если вы хотите скопировать реквизиты на существующий объект, просто установите destarg.

mpen
источник
0

Это вроде жульничества, но можно и так ...

const originalObject = {
  hello: 'nurse',
  meaningOfLife: 42,
  your: 'mom',
};

const partialObject = (({ hello, your }) => {
  return { hello, your };
})(originalObject);

console.log(partialObject); // ​​​​​{ hello: 'nurse', your: 'mom' }​​​​​

На практике, я думаю, вы вряд ли захотите это использовать. Следующее - НАМНОГО яснее ... но не так весело.

const partialObject = {
  hello: originalObject.hello,
  your: originalObject.your,
};

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

if (!Object.prototype.pluck) {
  Object.prototype.pluck = function(...props) {
    return props.reduce((destObj, prop) => {
      destObj[prop] = this[prop];

      return destObj;
    }, {});
  }
}

const originalObject = {
  hello: 'nurse',
  meaningOfLife: 42,
  your: 'mom',
};

const partialObject2 = originalObject.pluck('hello', 'your');

console.log(partialObject2); // { hello: 'nurse', your: 'mom' }
Барт
источник
0

Это самое удобочитаемое и кратчайшее решение, которое я мог придумать:

let props = { 
  isValidDate: 'yes',
  badProp: 'no!',
};

let { isValidDate } = props;
let newProps = { isValidDate };

console.log(newProps);

Он выведет { isValidDate: 'yes' }

Было бы неплохо когда-нибудь сказать что-то подобное, let newProps = ({ isValidDate } = props)но, к сожалению, это не то, что поддерживает ES6.

Патрик Майклсен
источник
0

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

const myObject = {
  name: 'foo',
  surname: 'bar',
  year: 2018
};

const newObject = ['name', 'surname'].reduce(
  (prev, curr) => (prev[curr] = myObject[curr], prev),
  {},
);

console.log(JSON.stringify(newObject)); // {"name":"foo","surname":"bar"}
Sornii
источник
0

Вы можете использовать методы класса JSON для достижения этого следующим образом

const foo = {
   x: "bar",
   y: "baz"
};

const oof = JSON.parse(JSON.stringify(foo, ['x','y']));
// output -> {x: "bar", y: "baz"}

Передайте свойства, которые необходимо добавить к результирующему объекту, в качестве второго аргумента stringifyфункции в формате массива.

Документ MDN для JSON.stringify

Бхаратвадж Ганешан
источник