typescript - วัตถุโคลน


187

ฉันมีระดับสุดที่เป็นผู้ปกครอง ( Entity) หลายประเภทรอง ( Customer, Product, ProductCategory... )

ฉันต้องการโคลนแบบไดนามิกวัตถุที่มีวัตถุย่อยที่แตกต่างกันใน typescript

ในตัวอย่าง: a Customerที่มีความแตกต่างProductใครมีProductCategory

var cust:Customer  = new Customer ();

cust.name = "someName";
cust.products.push(new Product(someId1));
cust.products.push(new Product(someId2));

เพื่อที่จะโคลนต้นไม้ทั้งหมดของวัตถุฉันสร้างฟังก์ชั่นมา Entity

public clone():any {
    var cloneObj = new this.constructor();
    for (var attribut in this) {
        if(typeof this[attribut] === "object"){
           cloneObj[attribut] = this.clone();
        } else {
           cloneObj[attribut] = this[attribut];
        }
    }
    return cloneObj;
}

newเพิ่มขึ้นข้อผิดพลาดต่อไปนี้เมื่อมีการ transpiled เพื่อ javascript:error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.

แม้ว่าสคริปต์ทำงานได้ แต่ฉันต้องการกำจัดข้อผิดพลาด transpiled

คำตอบ:


255

การแก้ปัญหาเฉพาะ

คุณสามารถใช้การยืนยันประเภทเพื่อบอกคอมไพเลอร์ว่าคุณรู้ดีกว่า:

public clone(): any {
    var cloneObj = new (this.constructor() as any);
    for (var attribut in this) {
        if (typeof this[attribut] === "object") {
            cloneObj[attribut] = this[attribut].clone();
        } else {
            cloneObj[attribut] = this[attribut];
        }
    }
    return cloneObj;
}

โคลน

โปรดจำไว้ว่าบางครั้งการเขียนแผนที่ของคุณจะดีกว่าการทำแผนที่แบบไดนามิก อย่างไรก็ตามมีเทคนิค "การโคลนนิ่ง" บางอย่างที่คุณสามารถใช้เพื่อให้เอฟเฟกต์ต่างกัน

ฉันจะใช้รหัสต่อไปนี้สำหรับตัวอย่างที่ตามมาทั้งหมด:

class Example {
  constructor(public type: string) {

  }
}

class Customer {
  constructor(public name: string, public example: Example) {

  }

  greet() {
    return 'Hello ' + this.name;
  }
}

var customer = new Customer('David', new Example('DavidType'));

ตัวเลือกที่ 1: การแพร่กระจาย

คุณสมบัติ: ใช่
วิธีการ: ไม่
คัดลอกลึก: ไม่

var clone = { ...customer };

alert(clone.name + ' ' + clone.example.type); // David DavidType
//alert(clone.greet()); // Not OK

clone.name = 'Steve';
clone.example.type = 'SteveType';

alert(customer.name + ' ' + customer.example.type); // David SteveType

ตัวเลือก 2: Object.assign

คุณสมบัติ: ใช่
วิธีการ: ไม่
คัดลอกลึก: ไม่

var clone = Object.assign({}, customer);

alert(clone.name + ' ' + clone.example.type); // David DavidType
alert(clone.greet()); // Not OK, although compiler won't spot it

clone.name = 'Steve';
clone.example.type = 'SteveType';

alert(customer.name + ' ' + customer.example.type); // David SteveType

ตัวเลือก 3: Object.create

คุณสมบัติ: วิธีการรับช่วง
: รับ
สำเนาลึก: สืบทอดตื้น (การเปลี่ยนแปลงที่ลึกส่งผลกระทบต่อทั้งต้นฉบับและโคลน)

var clone = Object.create(customer);

alert(clone.name + ' ' + clone.example.type); // David DavidType
alert(clone.greet()); // OK

customer.name = 'Misha';
customer.example = new Example("MishaType");

// clone sees changes to original 
alert(clone.name + ' ' + clone.example.type); // Misha MishaType

clone.name = 'Steve';
clone.example.type = 'SteveType';

// original sees changes to clone
alert(customer.name + ' ' + customer.example.type); // Misha SteveType

ตัวเลือก 4: ฟังก์ชั่นการทำสำเนาลึก

คุณสมบัติ: ใช่
วิธีการ: ไม่
คัดลอกลึก: ใช่

function deepCopy(obj) {
    var copy;

    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = deepCopy(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = deepCopy(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
}

var clone = deepCopy(customer) as Customer;

alert(clone.name + ' ' + clone.example.type); // David DavidType
// alert(clone.greet()); // Not OK - not really a customer

clone.name = 'Steve';
clone.example.type = 'SteveType';

alert(customer.name + ' ' + customer.example.type); // David DavidType

ปิด transpile หยุดบ่นกับ typescript 1.3 แต่ครั้งเดียวในจาวาสคริปต์มันจะโยนข้อผิดพลาด typescript 1.4.1 จะไม่ปล่อยให้มันไป
David Laberge

1
คุณจะได้ Abel ชี้แจงว่าคุณใช้สิ่งนี้ได้อย่างไร ฉันรวมเป็นวิธีการของวัตถุของฉันแล้วได้รับข้อผิดพลาดว่าไม่ใช่ฟังก์ชั่น ...
megalucio

1
ฉันได้รับข้อผิดพลาดดังต่อไปนี้: "ข้อผิดพลาด TypeError: this.constructor (... ) ไม่ใช่ตัวสร้าง"
michali

3
คุณเพิ่งทำตัวอย่างสาธารณะจากลูกค้ารายนั้นหรือไม่?
แบลร์คอนนอลลี่

1
ใครบางคนสามารถ TL; DR สำหรับฉันได้ซึ่งโซลูชั่นใดที่ให้ไว้ในคำตอบทั้งหมดรักษาโคลนประเภท OO เช่นcloned instanceof MyClass === true?
Szczepan Hołyszewski

177

1. ใช้ตัวดำเนินการสเปรด

const obj1 = { param: "value" };
const obj2 = { ...obj1 };

ตัวดำเนินการกระจายใช้ฟิลด์ทั้งหมดจาก obj1 และแพร่กระจายผ่าน obj2 ในผลลัพธ์คุณจะได้รับวัตถุใหม่พร้อมการอ้างอิงใหม่และเขตข้อมูลเดียวกับของเดิม

โปรดจำไว้ว่ามันเป็นสำเนาตื้นมันหมายความว่าถ้าวัตถุซ้อนกันแล้วประกอบซ้อนกันของมันจะอยู่ในวัตถุใหม่โดยการอ้างอิงเดียวกัน

2.Object.assign ()

const obj1={ param: "value" };
const obj2:any = Object.assign({}, obj1);

Object.assignสร้างสำเนาจริง แต่มีคุณสมบัติของตัวเองเท่านั้นดังนั้นคุณสมบัติในต้นแบบจะไม่มีอยู่ในวัตถุที่คัดลอก มันเป็นสำเนาตื้น ๆ


3.Object.create ()

const obj1={ param: "value" };
const obj2:any = Object.create(obj1);

Object.create ไม่ได้ทำการโคลนจริงมันกำลังสร้างวัตถุจากต้นแบบ ดังนั้นใช้มันถ้าวัตถุควรโคลนคุณสมบัติประเภทหลักเพราะการกำหนดคุณสมบัติประเภทหลักไม่ได้ทำโดยการอ้างอิง

ข้อดีของObject.createคือฟังก์ชั่นใด ๆ ที่ประกาศในต้นแบบจะมีอยู่ในวัตถุที่สร้างขึ้นใหม่ของเรา


บางสิ่งเกี่ยวกับการคัดลอกตื้น

การคัดลอกตื้นเข้าไปในวัตถุใหม่เขตข้อมูลทั้งหมดของเก่า แต่ก็หมายความว่าถ้าวัตถุต้นฉบับมีเขตข้อมูลชนิดประกอบ (วัตถุอาร์เรย์ ฯลฯ ) แล้วเขตข้อมูลเหล่านั้นจะถูกวางในวัตถุใหม่ที่มีการอ้างอิงเดียวกัน การกลายพันธุ์ของฟิลด์ดังกล่าวในวัตถุดั้งเดิมจะปรากฏในวัตถุใหม่

มันอาจดูเหมือนว่าเป็นหลุมพราง แต่จริง ๆ แล้วเมื่อวัตถุที่ซับซ้อนทั้งหมดต้องถูกคัดลอกนั้นหายาก การคัดลอกตื้นจะใช้หน่วยความจำส่วนใหญ่อีกครั้งซึ่งหมายความว่าราคาถูกมากเมื่อเปรียบเทียบกับการทำสำเนาลึก


สำเนาลึก

ผู้ประกอบการแพร่กระจายสามารถมีประโยชน์สำหรับการทำสำเนาลึก

const obj1 = { param: "value", complex: { name: "John"}}
const obj2 = { ...obj1, complex: {...obj1.complex}};

โค้ดด้านบนสร้างสำเนาลึกของ obj1 ฟิลด์คอมโพสิต "complex" ถูกคัดลอกไปยัง obj2 ด้วย ฟิลด์การกลายพันธุ์ "ซับซ้อน" จะไม่สะท้อนการคัดลอก


8
ฉันไม่คิดว่ามันถูกต้องทั้งหมด Object.create(obj1)สร้างวัตถุใหม่และกำหนด obj1 เป็นต้นแบบ ไม่มีฟิลด์ใด ๆ ใน obj1 ที่คัดลอกหรือโคลน ดังนั้นการเปลี่ยนแปลง obj1 ที่ไม่มีการแก้ไข obj2 จะถูกมองเห็นเนื่องจากส่วนใหญ่ไม่มีคุณสมบัติ หากคุณแก้ไข obj2 ก่อนระบบจะไม่เห็นต้นแบบสำหรับฟิลด์ที่คุณกำหนดเนื่องจากฟิลด์ของ obj2 ที่มีชื่อใกล้เคียงกับลำดับชั้น
Ken Rimple

3
คุณจะเห็น ES2015 และผู้พัฒนา typescript ทำเช่นนี้แทนซึ่งสร้างวัตถุจากพารามิเตอร์ที่ 1 (ในกรณีของฉันว่างเปล่า) และคัดลอกคุณสมบัติจากพารามิเตอร์ที่สองและที่ตามมา): let b = Object.assign({}, a);
Ken Rimple

@KenRimple คุณถูก 100% ฉันเพิ่มข้อมูลเพิ่มเติม
Maciej Sikora

อาจจะมีประโยชน์ => developer.mozilla.org/en/docs/Web/JavaScript/Reference/
......

5
Object.assign จะสร้างปัญหาสำหรับวัตถุลึก ตัวอย่างเช่น {name: 'x', ค่า: ['a', 'b', 'c']} หลังจากใช้ Object.assign to clone วัตถุทั้งสองจะใช้อาร์เรย์ค่าร่วมกัน ดู: developer.mozilla.org/en/docs/Web/JavaScript/Reference/… (ส่วน 'คำเตือนสำหรับ Deep Clone') มันบอกว่า: สำหรับการโคลนนิ่งลึกเราต้องใช้ทางเลือกอื่น นี่เป็นเพราะ Object.assign () คัดลอกการอ้างอิงคุณสมบัติเมื่อคุณสมบัติที่ได้รับมอบหมายเป็นวัตถุ
เมียร์

48

ลองสิ่งนี้:

let copy = (JSON.parse(JSON.stringify(objectToCopy)));

มันเป็นวิธีการแก้ปัญหาที่ดีจนกว่าคุณจะใช้วัตถุที่มีขนาดใหญ่มากหรือวัตถุของคุณมีคุณสมบัติที่ไม่สามารถปรับแต่งได้

เพื่อรักษาความปลอดภัยของประเภทคุณสามารถใช้ฟังก์ชั่นการคัดลอกในชั้นเรียนที่คุณต้องการทำสำเนาจาก:

getCopy(): YourClassName{
    return (JSON.parse(JSON.stringify(this)));
}

หรือในทางคงที่:

static createCopy(objectToCopy: YourClassName): YourClassName{
    return (JSON.parse(JSON.stringify(objectToCopy)));
}

5
นี่เป็นเรื่องปกติ แต่คุณควรจำไว้ว่าคุณจะสูญเสียข้อมูลต้นแบบและทุกประเภทไม่ได้รับการสนับสนุนใน json เมื่อทำให้เป็นอันดับ / แยกวิเคราะห์
Stanislav E. Govorov

1
นอกจากนี้ดูเหมือนว่ามีประสิทธิภาพน้อยกว่าเมื่อเทียบกับฟังก์ชั่น deepcopy ให้ไว้ข้างต้น
Mojtaba

ฉันมีข้อผิดพลาดนี้: "การแปลงโครงสร้างวงกลมเป็น JSON" เมื่อฉันใช้ "(JSON.parse (JSON.stringify (objectToCopy)));"
Cedric Arnould

ใช้งานได้ใน 98% ของคดีเท่านั้น สามารถนำไปสู่คีย์ที่หายไปซึ่งมีundefinedค่าอย่างน้อย ถ้าobjectToCopy = { x : undefined};แล้วหลังจากที่ใช้รหัสของคุณObject.keys(objectToCopy).lengthคือ1ในขณะที่มีObject.keys(copy).length 0
Aidin

33

TypeScript / JavaScript มีโอเปอเรเตอร์ของตัวเองสำหรับการโคลนแบบตื้น:

let shallowClone = { ...original };

15

เป็นเรื่องง่ายที่จะได้รับสำเนาตื้น ๆ ด้วย "Object Spread" ที่แนะนำใน TypeScript 2.1

TypeScript นี้: let copy = { ...original };

สร้าง JavaScript นี้:

var __assign = (this && this.__assign) || Object.assign || function(t) {
    for (var s, i = 1, n = arguments.length; i < n; i++) {
        s = arguments[i];
        for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
            t[p] = s[p];
    }
    return t;
};
var copy = __assign({}, original);

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html


2
หมายเหตุ: สิ่งนี้จะสร้างสำเนาตื้น
Jimmy Kane

11

สำหรับการโคลนแบบลึกที่สามารถทำให้เป็นอนุกรมที่มีข้อมูลประเภทคือ

export function clone<T>(a: T): T {
  return JSON.parse(JSON.stringify(a));
}

1
สิ่งนี้สามารถเปลี่ยนลำดับของอุปกรณ์ประกอบฉาก เพียงแค่คำเตือนสำหรับบางคน นอกจากนี้ยังไม่สามารถจัดการวันที่ได้อย่างถูกต้อง
Pangamma

สิ่งนี้สามารถเปลี่ยนลำดับของอุปกรณ์ประกอบฉาก - อาจลองnpmjs.com/package/es6-json-stable-stringifyแทนJSON.stringify
Polv

@Polv cloneถ้ามีคนจะอาศัยคำสั่งของคีย์ในวัตถุที่ฉันคิดว่าพวกเขามีปัญหาที่ใหญ่กว่า :)
Aidin

วิธีแก้ปัญหานี้อาจทำให้พลาดกุญแจที่มีundefinedค่า ดูความคิดเห็นของฉันเกี่ยวกับคำตอบที่คล้ายกันข้างต้น: stackoverflow.com/questions/28150967/typescript-cloning-object/…
Aidin

7

ฉันใช้มัน:

Object.assign(...) คัดลอกคุณสมบัติเท่านั้นและเราจะสูญเสียต้นแบบและวิธีการ

Object.create(...) ไม่ได้คัดลอกคุณสมบัติสำหรับฉันและเพียงแค่สร้างต้นแบบ

สิ่งที่ใช้ได้ผลสำหรับฉันคือการสร้างต้นแบบโดยใช้Object.create(...)และคัดลอกคุณสมบัติไปใช้โดยObject.assign(...):

ดังนั้นสำหรับวัตถุfooทำโคลนเช่นนี้

Object.assign(Object.create(foo), foo)

มีสิ่งที่ละเอียดมากเกิดขึ้นที่นี่ คุณกำลังทำตัวfooเป็นแม่แบบต้นแบบของclonedFoo(วัตถุใหม่) ในขณะนี้อาจฟังดูโอเคคุณควรจำไว้ว่าทรัพย์สินที่หายไปจะถูกค้นหาในห่วงโซ่ต้นแบบดังนั้นจึงconst a = { x: 8 }; const c = Object.assign(Object.create(a), a); delete c.x; console.log(c.x);พิมพ์ 8 ในขณะที่ควรจะเป็นundefined! (ลิงก์ REPL: repl.it/repls/CompetitivePreemptiveKeygen )
Aidin

นอกจากนี้หากคุณเพิ่มคุณสมบัติในภายหลังคุณสมบัติfooจะปรากฏขึ้นโดยอัตโนมัติclonedFoo! เช่นfoo.y = 9; console.log(clonedFoo.y)จะพิมพ์ออกมาแทน9 undefinedมีโอกาสมากที่ไม่ใช่สิ่งที่คุณขอ!
Aidin

@Aidin ดังนั้นวิธีการตรวจสอบสำเนาลึก
มูฮัมหมัดอาลี

โซลูชันอื่นใดในคำถามนี้ซึ่งกำลังทำซ้ำโดยคัดลอกค่าซ้ำ (เช่นstackoverflow.com/a/53025968โดย marckassay) รับรองว่าเนื่องจากไม่มีการอ้างอิงถึงวัตถุต้นฉบับที่ถูกเก็บรักษาไว้ในวัตถุเป้าหมาย
Aidin

5

คุณสามารถมีสิ่งนี้:

class Entity {
    id: number;

    constructor(id: number) {
        this.id = id;
    }

    clone(): this {
        return new (this.constructor as typeof Entity)(this.id) as this;
    }
}

class Customer extends Entity {
    name: string;

    constructor(id: number, name: string) {
        super(id);
        this.name = name;
    }

    clone(): this {
        return new (this.constructor as typeof Customer)(this.id, this.name) as this;
    }
}

เพียงให้แน่ใจว่าคุณแทนที่cloneวิธีการในEntityคลาสย่อยทั้งหมดมิฉะนั้นคุณจะจบลงด้วยการโคลนบางส่วน

ประเภทส่งคืนของthisจะตรงกับประเภทของอินสแตนซ์เสมอ


4

เพิ่มที่คุณ"lodash.clonedeep": "^4.5.0" package.jsonจากนั้นใช้ดังนี้:

import * as _ from 'lodash';

...

const copy = _.cloneDeep(original)

3

นี่คือส่วนผสมของฉัน! และนี่คือการเชื่อมโยง StackBlitzกับมัน ในปัจจุบันมีข้อ จำกัด เพียงแค่การคัดลอกประเภทอย่างง่ายและประเภทวัตถุ แต่สามารถแก้ไขได้อย่างง่ายดายฉันคิด

   let deepClone = <T>(source: T): { [k: string]: any } => {
      let results: { [k: string]: any } = {};
      for (let P in source) {
        if (typeof source[P] === 'object') {
          results[P] = deepClone(source[P]);
        } else {
          results[P] = source[P];
        }
      }
      return results;
    };

1
ใช้งานได้ดีเท่าที่ฉันเห็น อย่างไรก็ตามtypeof nullยังเป็นวัตถุดังนั้นแบบสอบถามควรจะif (source[P] !== null && typeof source[P] === 'object')แทน มิฉะนั้นค่า Null ของคุณจะกลายเป็นวัตถุว่างเปล่า
MortenMoulder

3

หากคุณได้รับข้อผิดพลาดนี้:

TypeError: this.constructor(...) is not a function

นี่คือสคริปต์ที่ถูกต้อง:

public clone(): any {
    var cloneObj = new (<any>this.constructor)(); // line fixed
    for (var attribut in this) {
        if (typeof this[attribut] === "object") {
            cloneObj[attribut] = this[attribut].clone();
        } else {
            cloneObj[attribut] = this[attribut];
        }
    }
    return cloneObj;
}

4
ถูกต้องcloneObj[attribut] = this.clone();หรือไม่ หรือคุณหมายถึงcloneObj[attribut] = this[attribut].clone();
Serginho

2

มาข้ามปัญหานี้ด้วยตนเองและในที่สุดก็เขียนห้องสมุดขนาดเล็กcloneable-tsที่ให้คลาสนามธรรมซึ่งเพิ่มวิธีการโคลนไปยังชั้นเรียนใด ๆ ที่ขยายมัน คลาสนามธรรมยืมฟังก์ชัน Deep Copy ที่อธิบายในคำตอบที่ยอมรับโดย Fenton เพียงแทนที่copy = {};ด้วยcopy = Object.create(originalObj)เพื่อรักษาคลาสของวัตถุต้นฉบับ นี่คือตัวอย่างของการใช้คลาส

import {Cloneable, CloneableArgs} from 'cloneable-ts';

// Interface that will be used as named arguments to initialize and clone an object
interface PersonArgs {
    readonly name: string;
    readonly age: number;
}

// Cloneable abstract class initializes the object with super method and adds the clone method
// CloneableArgs interface ensures that all properties defined in the argument interface are defined in class
class Person extends Cloneable<TestArgs>  implements CloneableArgs<PersonArgs> {
    readonly name: string;
    readonly age: number;

    constructor(args: TestArgs) {
        super(args);
    }
}

const a = new Person({name: 'Alice', age: 28});
const b = a.clone({name: 'Bob'})
a.name // Alice
b.name // Bob
b.age // 28

หรือคุณอาจใช้Cloneable.cloneวิธีการช่วยเหลือ:

import {Cloneable} from 'cloneable-ts';

interface Person {
    readonly name: string;
    readonly age: number;
}

const a: Person = {name: 'Alice', age: 28};
const b = Cloneable.clone(a, {name: 'Bob'})
a.name // Alice
b.name // Bob
b.age // 28    

1

เนื่องจาก TypeScript 3.7 ถูกนำออกใช้ขณะนี้สนับสนุนนามแฝงประเภท recursiveและช่วยให้เราสามารถกำหนดประเภทdeepCopy()ฟังก์ชั่นที่ปลอดภัย:

// DeepCopy type can be easily extended by other types,
// like Set & Map if the implementation supports them.
type DeepCopy<T> =
    T extends undefined | null | boolean | string | number ? T :
    T extends Function | Set<any> | Map<any, any> ? unknown :
    T extends ReadonlyArray<infer U> ? Array<DeepCopy<U>> :
    { [K in keyof T]: DeepCopy<T[K]> };

function deepCopy<T>(obj: T): DeepCopy<T> {
    // implementation doesn't matter, just use the simplest
    return JSON.parse(JSON.stringify(obj));
}

interface User {
    name: string,
    achievements: readonly string[],
    extras?: {
        city: string;
    }
}

type UncopiableUser = User & {
    delete: () => void
};

declare const user: User;
const userCopy: User = deepCopy(user); // no errors

declare const uncopiableUser: UncopiableUser;
const uncopiableUserCopy: UncopiableUser = deepCopy(uncopiableUser); // compile time error

สนามเด็กเล่น


0

สำหรับเนื้อหาโคลด์รูของออบเจ็กต์ที่เรียบง่ายฉันเพียงแค่ระบุและแยกอินสแตนซ์:

let cloneObject = JSON.parse(JSON.stringify(objectToClone))

ในขณะที่ฉันเปลี่ยนข้อมูลในแผนผัง objectToClone ไม่มีการเปลี่ยนแปลงใน cloneObject นั่นคือความต้องการของฉัน

หวังว่ามันจะช่วย


1
สามารถพลาดปุ่มที่มีundefinedค่า ดูความคิดเห็นของฉันเกี่ยวกับคำตอบที่คล้ายกันข้างต้น: stackoverflow.com/questions/28150967/typescript-cloning-object/…
Aidin

0

ฉันลงเอยด้วยการทำ:

public clone(): any {
  const result = new (<any>this.constructor);

  // some deserialization code I hade in place already...
  // which deep copies all serialized properties of the
  // object graph
  // result.deserialize(this)

  // you could use any of the usggestions in the other answers to
  // copy over all the desired fields / properties

  return result;
}

เพราะ:

var cloneObj = new (<any>this.constructor());

จาก @Fenton ให้ข้อผิดพลาดรันไทม์

รุ่นของตัวอักษร: 2.4.2


0

jQuery แบบเก่าดีแค่ไหน! นี่คือโคลนลึก:

var clone = $.extend(true, {}, sourceObject);

คำถามนี้ไม่ได้ติดแท็ก JQuery และไม่ได้พูดถึง JQuery ในคำถาม นอกจากนี้ยังจะเป็นค่าใช้จ่ายมากในการรวม JQuery ในโครงการเพื่อทำการโคลนนิ่งลึก
LewisM

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

0

ฉันเอาแทงไปสร้างบริการคัดลอก / โคลนทั่วไปที่ยังคงประเภทสำหรับวัตถุที่ซ้อนกัน จะชอบความคิดเห็นถ้าฉันทำอะไรผิด แต่ดูเหมือนว่ามันจะได้ผล ...

import { Injectable } from '@angular/core';

@Injectable()
export class CopyService {

  public deepCopy<T>(objectToClone: T): T {
    // If it's a simple type or null, just return it.
    if (typeof objectToClone === 'string' ||
      typeof objectToClone === 'number' ||
      typeof objectToClone === 'undefined' ||
      typeof objectToClone === 'symbol' ||
      typeof objectToClone === 'function' ||
      typeof objectToClone === 'boolean' ||
      objectToClone === null
    ) {
      return objectToClone;
    }

    // Otherwise, check if it has a constructor we can use to properly instantiate it...
    let ctor = Object.getPrototypeOf(objectToClone).constructor;
    if (ctor) {
      let clone = new ctor();

      // Once we've instantiated the correct type, assign the child properties with deep copies of the values
      Object.keys(objectToClone).forEach(key => {
        if (Array.isArray(objectToClone[key]))
          clone[key] = objectToClone[key].map(item => this.deepCopy(item));
        else
          clone[key] = this.deepCopy(objectToClone[key]);
      });

      if (JSON.stringify(objectToClone) !== JSON.stringify(clone))
        console.warn('object cloned, but doesnt match exactly...\nobject: ' + JSON.stringify(objectToClone) + "\nclone: " + JSON.stringify(clone))

      // return our cloned object...
      return clone;
    }
    else {
      //not sure this will ever get hit, but figured I'd have a catch call.
      console.log('deep copy found something it didnt know: ' + JSON.stringify(objectToClone));
      return objectToClone;
    }
  }
}

0

ใน typeScript ฉันทดสอบกับเชิงมุมและมันก็โอเค

deepCopy(obj) {


        var copy;

        // Handle the 3 simple types, and null or undefined
        if (null == obj || "object" != typeof obj) return obj;

        // Handle Date
        if (obj instanceof Date) {
            copy = new Date();
            copy.setTime(obj.getTime());
            return copy;
        }

        // Handle Array
        if (obj instanceof Array) {
            copy = [];
            for (var i = 0, len = obj.length; i < len; i++) {
                copy[i] = this.deepCopy(obj[i]);
            }
            return copy;
        }

        // Handle Object
        if (obj instanceof Object) {
            copy = {};
            for (var attr in obj) {
                if (obj.hasOwnProperty(attr)) copy[attr] = this.deepCopy(obj[attr]);
            }
            return copy;
        }

        throw new Error("Unable to copy obj! Its type isn't supported.");
    }

0

สำหรับการโคลนวัตถุที่สามารถบรรจุวัตถุอื่นได้อาร์เรย์และอื่น ๆ ที่ฉันใช้:

const clone = <T>(source: T): T => {
  if (source === null) return source

  if (source instanceof Date) return new Date(source.getTime()) as any

  if (source instanceof Array) return source.map((item: any) => clone<any>(item)) as any

  if (typeof source === 'object' && source !== {}) {
    const clonnedObj = { ...(source as { [key: string]: any }) } as { [key: string]: any }
    Object.keys(clonnedObj).forEach(prop => {
      clonnedObj[prop] = clone<any>(clonnedObj[prop])
    })

    return clonnedObj as T
  }

  return source
}

ใช้:

const obj = {a: [1,2], b: 's', c: () => { return 'h'; }, d: null, e: {a:['x'] }}
const objClone = clone(obj)

0

คุณสามารถใช้การกำหนดรูปแบบการทำลายที่มีไวยากรณ์การแพร่กระจาย :

var obj = {id = 1, name = 'product1'};
var clonedObject = {...obj};

1
ในขณะที่รหัสนี้อาจตอบคำถามให้บริบทเพิ่มเติมเกี่ยวกับวิธีการและ / หรือทำไมมันแก้ปัญหาจะปรับปรุงค่าระยะยาวของคำตอบ
leopal

-2

หากคุณมีวัตถุเป้าหมายอยู่แล้วดังนั้นคุณไม่ต้องการสร้างมันขึ้นมาใหม่ (เช่นถ้าอัปเดตอาร์เรย์) คุณต้องคัดลอกคุณสมบัติ
หากทำอย่างนี้:

Object.keys(source).forEach((key) => {
    copy[key] = source[key]
})

สรรเสริญเป็นเพราะ (ดูหัวข้อ "รุ่น 2")


ฟังก์ชั่น? อาร์เรย์? วันที่ของวัตถุ? การอนุรักษ์ประเภท? และแน่นอนสิ่งที่เกี่ยวกับวัตถุ? หากฟังก์ชั่นด้านบนพบกับประเภทใด ๆ ข้างต้นมันจะล้มเหลวในการโคลนลึก คุณจะได้คัดลอกข้อมูลอ้างอิงไปยังข้อมูลเดียวกัน เมื่อพวกเขาไปแก้ไขคุณสมบัติลูกของวัตถุที่ถูกโคลนพวกเขาก็จะทำการแก้ไขวัตถุต้นฉบับเช่นกัน
Pangamma
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.