เป็นไปได้ไหมที่จะทำลายโครงสร้างลงบนวัตถุที่มีอยู่ (Javascript ES6)


135

เช่นถ้าฉันมีวัตถุสองชิ้น:

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

และ

var oof = {}

และฉันต้องการถ่ายโอนค่า x และ y จาก foo ไปยัง oof มีวิธีการทำเช่นนั้นโดยใช้ไวยากรณ์ destructuring es6 หรือไม่?

อาจจะชอบ:

oof{x,y} = foo

10
หากคุณต้องการที่จะคัดลอกทุกคุณสมบัติObject.assign(oof, foo)
elclanrs

ตัวอย่างการทำลายล้างที่นี่มากมายบน MDNแต่ฉันไม่เห็นสิ่งที่คุณกำลังถาม
jfriend00

อืมฉันหาไม่เจอเหมือนกัน ..
เมเจอร์ Bummer

ดูคำตอบของฉันด้านล่างเพื่อแก้ปัญหาสองบรรทัดโดยไม่ต้อง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 (อย่าทำซ้ำตัวเอง) จนถึงตอนนี้ "DRY" จะมีประโยชน์แน่นอนในกรณีที่มีการจับคู่คีย์หลาย ๆ อัน คุณจะต้องอัปเดตที่เดียวเท่านั้น บางทีฉันผิด ขอขอบคุณ!
Vladimir Brasil

1
ตัวอย่างที่สามคือ DRY แต่คุณต้องใช้สตริงเป็นคีย์ซึ่งโดยปกติแล้วจาวาสคริปต์จะกระทำโดยปริยาย ไม่เชื่อว่าเป็นเรื่องน่ารำคาญน้อยกว่า: const {x, y} = foo; const oof = {x, y};
Jeff Lowery

ใครสามารถแสดงให้ฉันเห็นใน 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: เขาทำอย่างนั้น (ตอนนี้) Bergi น่าจะดีที่สุดในการอัพเดทคำตอบให้โทรหาคุณสามารถใช้การแสดงออกของสมาชิกตามที่โลแกนแสดง (จริงหรือไม่ที่ข้อมูลจำเพาะเปลี่ยนไปมากระหว่างเดือนเมษายนและมิถุนายน 2558)
TJ Crowder

@TJCrowder {oof.x, oof.y} = fooไม่ฉันไม่คิดว่ามันเปลี่ยนไปผมคิดว่าผมได้พูดคุยเกี่ยวกับ หรือบางทีฉันอาจจะพลาดมันจริงๆ
Bergi

29

IMO นี่เป็นวิธีที่ง่ายที่สุดในการบรรลุสิ่งที่คุณต้องการ:

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

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

โดยพื้นฐานแล้วให้โครงสร้างเป็นตัวแปรแล้วใช้ initializer ชวเลขเพื่อสร้างวัตถุใหม่ ไม่ต้องการ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 };

ฉันชอบวิธีนี้ในทางปฏิบัติ
เจสซี่

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


9
สิ่งนี้ไม่ได้ตั้งค่าคุณสมบัติบนวัตถุที่มีอยู่ แต่สร้างวัตถุใหม่ที่มีคุณสมบัติทั้งหมดของทั้งสอง
Matt Browne

8

ปลั๊กอิน BabelJS

หากคุณใช้BabelJSคุณสามารถเปิดใช้งานปลั๊กอินของฉันได้babel-plugin-transform-object-from-destructuring( ดูแพ็คเกจสำหรับการติดตั้งและการใช้งาน )

ฉันมีปัญหาเดียวกันที่อธิบายไว้ในหัวข้อนี้และสำหรับฉันมันเหนื่อยมากเมื่อคุณสร้างวัตถุจากนิพจน์การทำลายล้างโดยเฉพาะอย่างยิ่งเมื่อคุณต้องเปลี่ยนชื่อเพิ่มหรือลบคุณสมบัติ ด้วยปลั๊กอินนี้การบำรุงรักษาสถานการณ์ดังกล่าวทำให้คุณง่ายขึ้น

ตัวอย่างวัตถุ

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)

ฉันคิดว่ากรณีแรกนี้คือสิ่งที่ฉันกำลังมองหา ฉันลองใช้ในคอนโซล Chrome และดูเหมือนว่าจะใช้งานได้ ไชโย! @campsafari
majorBummer

1
@majorBummer การโทรของคุณในท้ายที่สุด แต่ฉันจะพิจารณาวิธีที่จะไม่ตอบคำถามของคุณเพราะทั้งคู่สร้างวัตถุใหม่แทนที่จะเพิ่มคุณสมบัติให้กับวัตถุที่มีอยู่เหมือนที่คุณถาม
loganfsmyth

นั่นเป็นจุดที่ดี @loganfsmyth ฉันคิดว่าฉันรู้สึกตื่นเต้นกับวิธีที่ค่ายซาฟารีเลี้ยงดูมาเพราะฉันไม่ได้ตระหนักว่ามันเป็นไปได้
MajorBummer

1
หากคุณไม่สนใจที่จะสร้างวัตถุใหม่คุณสามารถทำได้oof = {...oof, ...foo}
Joshua Coady

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

ใช้งานได้ใน 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"
}

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คุณยังสามารถเปลี่ยนชื่อพวกเขาในเวลาเดียวกัน

หากคุณต้องการคัดลอกอุปกรณ์ประกอบฉากไปยังวัตถุที่มีอยู่เพียงแค่ตั้งค่าdestหาเรื่อง


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 Doc สำหรับ JSON.stringify

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.