Чи можна зруйнувати існуючий об’єкт? (Javascript ES6)


135

Наприклад, якщо у мене є два об'єкти:

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

і

var oof = {}

і я хотів перенести значення x і y з foo в oof. Чи є спосіб зробити це, використовуючи синтаксис деструктуризації es6?

можливо щось на кшталт:

oof{x,y} = foo

10
Якщо ви хочете скопіювати всі властивостіObject.assign(oof, foo)
elclanrs

Багато прикладів руйнування тут на MDN , але я не бачу того, про кого ви питаєте.
jfriend00

Гм, я теж не зміг знайти ...
majorBummer

Дивіться мою відповідь нижче щодо дворядкового рішення безObject.assign
Zfalen

Відповіді:


116

Хоча потворно і трохи повторюється, ви можете зробити

({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]);

хоч.


2
Альтернатива ['x', 'y']. ForEach (prop => oof [prop] = foo [prop]); як я бачу, є єдиним СУХОМ (не повторюйте себе), до цих пір. "DRY" був би дійсно корисним у випадку декількох ключів, які потрібно відобразити. Вам потрібно буде оновити лише одне місце. Можливо, я помиляюся. Все одно, дякую!
Володимир Бразиль

1
Третій приклад - DRY, але ви повинні використовувати рядки в якості клавіш, що JavaScript зазвичай робить неявно. Не впевнений, що це менше неприємностей, ніж: const {x, y} = foo; const oof = {x, y};
Джефф Лоурі

Чи може хтось показати мені у jsfiddle, де працює перша пропозиція? Тут не працює хром: jsfiddle.net/7mh399ow
techguy2000

@ techguy2000 Ви забули крапку з комою після var oof = {};.
loganfsmyth

У першому коді не повинно бути ({x: oof.x, y: oof.y} = foo)? Я думаю , що ви поклали додатковий п в OOF .
Сорній

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. А може, я справді пропустив це.
Бергі

29

IMO Це найпростіший спосіб досягти того, що ви шукаєте:

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дуже часто використовую це для запитів, де мені потрібно лише кілька фрагментів дискретних даних. Але я погоджуюся, що для цього повинен бути один лайнер.

EDIT: 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 };

Мені подобається такий підхід, прагматичний.
Джессі

2
Це я роблю, але це не дуже сухо. Я думаю, що дійсно потрібно багато людей - це лише функція вилучення ключів.
jgmjgm

@jgmjgm Так, було б добре мати один вбудований, але я думаю, це теж не важко написати.
Зфален

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. Ось демонстрація на бабеля зstage-2


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
majorBummer

1
@majorBummer Зрештою, ваш дзвінок, але я б розглядав підхід, щоб насправді не відповісти на ваше запитання, оскільки вони обидва створюють нові об’єкти, а не додають властивості до існуючих об'єктів, як-от запитує ваша назва.
loganfsmyth

Це хороший момент @loganfsmyth. Я думаю, що мене просто схвилював метод табору Сафарі, який виховували, тому що я не розумів, що це можливо.
majorBummer

1
Якщо ви не проти створити новий об’єкт, ви могли б просто зробитиoof = {...oof, ...foo}
Джошуа Коаді

2

Ви можете повернути зруйнований об'єкт у функції стрілки та скористатися Object.assign (), щоб призначити його змінній.

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

const oof = Object.assign({}, () => ({ x, y } = foo));

1

СУХИЙ

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})()});

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


0

Це працює в хромі 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"
}

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.


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"}

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.