Câu hỏi typecript - nhân bản đối tượng


Tôi có một lớp học siêu đó là phụ huynh (Entity) cho nhiều phân lớp (Customer, Product, ProductCategory...)

Tôi đang tìm cách sao chép động một đối tượng có chứa các đối tượng con khác nhau trong Typescript.

Ví dụ: a Customer điều đó khác Product ai có ProductCategory

var cust:Customer  = new Customer ();

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

Để sao chép toàn bộ cây đối tượng, tôi đã tạo ra một hàm trong 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;
}

Các new tăng lỗi sau khi nó được chuyển thành javascript: error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.

Mặc dù kịch bản hoạt động, Tôi muốn loại bỏ các lỗi transpiled


63
2018-01-26 13:25


gốc




Các câu trả lời:


Giải quyết vấn đề cụ thể

Bạn có thể sử dụng một kiểu xác nhận để báo cho trình biên dịch biết rằng bạn biết rõ hơn:

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

Nhân bản

Ghi nhớ rằng đôi khi tốt hơn là viết bản đồ của riêng bạn - chứ không phải là hoàn toàn năng động. Tuy nhiên, có một vài thủ thuật "nhân bản" bạn có thể sử dụng cho bạn những hiệu ứng khác biệt.

Tôi sẽ sử dụng đoạn mã sau cho tất cả các ví dụ sau:

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'));

Tùy chọn 1: Lây lan

Tính chất: Vâng Phương pháp: Không Bản sao sâu: Không

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

Tùy chọn 2: Object.assign

Tính chất: Vâng Phương pháp: Không Bản sao sâu: Không

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

Tùy chọn 3: Object.create

Tính chất: Vâng Phương thức: Vâng Bản sao sâu: Không

var clone = Object.create(customer);

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

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

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

Tùy chọn 4: Chức năng sao chép sâu

Tính chất: Vâng Phương pháp: Không Bản sao sâu: Vâng

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 = <Customer>deepCopy(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

83
2018-01-26 14:27



Đóng, các transpile ngừng phàn nàn với các loại 1.3, nhưng một lần trong javascript nó sẽ ném lỗi. Typecript 1.4.1, sẽ không để nó đi. - David Laberge
Bạn có thể sử dụng Generics để lấy lại kết quả của cùng loại mà bạn đang nhân bản. - robmcm
Bạn sẽ được abel để làm rõ làm thế nào để bạn chính xác sử dụng này? Tôi bao gồm như một phương pháp của đối tượng của tôi và sau đó có một lỗi nói không phải là một chức năng ... - megalucio
Tôi nhận được lỗi sau: "ERROR TypeError: this.constructor (...) không phải là một hàm tạo" - michali


1. Sử dụng nhà điều hành lan truyền

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

Spread nhà điều hành có tất cả các lĩnh vực từ obj1 và lây lan chúng trên obj2. Trong kết quả bạn nhận được đối tượng mới với tham chiếu mới và các trường giống với trường gốc.

Hãy nhớ rằng nó là bản sao nông, có nghĩa là nếu đối tượng được lồng nhau thì các tham số tổng hợp lồng nhau của nó sẽ tồn tại trong đối tượng mới bằng cùng một tham chiếu.

2.Object.assign ()

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

Object.assign tạo bản sao thực, nhưng chỉ thuộc tính riêng, vì vậy các thuộc tính trong nguyên mẫu sẽ không tồn tại trong đối tượng được sao chép. Nó cũng là bản sao nông.


3.Object.create ()

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

Object.create  không làm nhân bản thực, nó tạo ra đối tượng từ nguyên mẫu. Vì vậy, sử dụng nó nếu đối tượng nên nhân bản thuộc tính loại chính, bởi vì phân bổ thuộc tính loại chính không được thực hiện bằng cách tham chiếu.

Điểm cộng của Object.create là bất kỳ hàm nào được khai báo trong nguyên mẫu sẽ có sẵn trong đối tượng mới được tạo của chúng ta.


Vài điều về bản sao nông

Bản sao nông đặt vào đối tượng mới tất cả các trường cũ, nhưng nó cũng có nghĩa là nếu đối tượng gốc có các trường kiểu hỗn hợp (đối tượng, mảng vv) thì các trường đó được đặt trong đối tượng mới với cùng tham chiếu. Trường đột biến như vậy trong đối tượng gốc sẽ được phản ánh trong đối tượng mới.

Nó có thể trông giống như một cạm bẫy, nhưng thực sự tình hình khi toàn bộ đối tượng phức tạp cần phải được sao chép là rất hiếm. Bản sao nông sẽ tái sử dụng phần lớn bộ nhớ có nghĩa là rất rẻ so với bản sao sâu.


Bản sao sâu

Spread nhà điều hành có thể được thuận tiện cho bản sao sâu.

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

Đoạn mã trên tạo bản sao sâu của obj1. Trường tổng hợp "phức tạp" cũng được sao chép vào obj2. Trường đột biến "phức tạp" sẽ không phản ánh bản sao.


92
2017-08-10 13:27



Tôi không nghĩ điều đó hoàn toàn chính xác. Object.create(obj1) tạo một đối tượng mới và gán obj1 làm nguyên mẫu. Không có trường nào trong obj1 được sao chép hoặc nhân bản. Vì vậy, thay đổi trên obj1 mà không sửa đổi obj2 sẽ được nhìn thấy, vì nó về cơ bản không có thuộc tính. Nếu bạn sửa đổi obj2 trước, nguyên mẫu sẽ không được nhìn thấy cho trường bạn xác định từ trường obj2 với tên gần hơn trong cấu trúc phân cấp. - Ken Rimple
Bạn cũng sẽ thấy ES2015 và các nhà phát triển bản ghi làm việc này thay vào đó, tạo ra một đối tượng từ tham số thứ nhất (trong trường hợp của tôi là một tham số trống) và sao chép các thuộc tính từ tham số thứ hai và tiếp theo): let b = Object.assign({}, a); - Ken Rimple
@KenRimple Bạn đang ở 100% bên phải, tôi đã thêm một số thông tin. - Maciej Sikora
có thể hữu ích => developer.mozilla.org/en/docs/Web/JavaScript/Reference/… - Emmanuel Touzery
Object.assign sẽ tạo ra các vấn đề cho các đối tượng sâu. Ví dụ: {name: 'x', các giá trị: ['a', 'b', 'c']}. Sau khi sử dụng Object.assign để sao chép, cả hai đối tượng chia sẻ mảng giá trị để cập nhật một ảnh hưởng đến nhau. Xem: developer.mozilla.org/en/docs/Web/JavaScript/Reference/… ('Cảnh báo cho bản sao sâu'). Nó nói: Đối với nhân bản sâu, chúng ta cần phải sử dụng các lựa chọn thay thế khác. Điều này là do Object.assign () sao chép tham chiếu thuộc tính khi thuộc tính được gán là một đối tượng. - Meir


Thử cái này:

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

Nó là một giải pháp tốt cho đến khi bạn đang sử dụng các đối tượng rất lớn hoặc đối tượng của bạn có các thuộc tính unserializable.

Để bảo vệ an toàn loại, bạn có thể sử dụng chức năng sao chép trong lớp bạn muốn tạo bản sao từ:

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

hoặc theo cách tĩnh:

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

14
2018-03-13 07:06



Điều này là ok, nhưng bạn nên nhớ rằng bạn sẽ mất thông tin nguyên mẫu và tất cả các loại không được hỗ trợ trong json khi serialize / parse. - Stanislav E. Govorov


Thật dễ dàng để có được một bản sao nông với "Object Spread" được giới thiệu trong TypeScript 2.1

TypeScript này: let copy = { ...original };

tạo ra JavaScript này:

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


13
2018-03-02 22:39



Lưu ý: điều này sẽ tạo ra một bản sao nông - Jimmy Kane


Typescript / Javascript có toán tử riêng của nó để nhân bản nông:

let shallowClone = { ...original };

11
2018-02-20 10:45





Bạn cũng có thể có một cái gì đó như thế này:

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

Chỉ cần đảm bảo rằng bạn ghi đè lên clone phương pháp trong tất cả Entity các lớp con nếu không bạn sẽ kết thúc với các bản sao một phần.

Kiểu trả về của this sẽ luôn phù hợp với loại cá thể.


4
2017-08-02 04:44





Nếu bạn gặp lỗi này:

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

Đây là kịch bản chính xác:

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

2
2018-05-04 13:31



ĐúngcloneObj[attribut] = this.clone();? hoặc ý bạn là cloneObj[attribut] = this[attribut].clone(); - Serginho


Đối với một bản sao đơn giản của nội dung của đối tượng lỗ, tôi chỉ đơn giản là xâu chuỗi và phân tích cú pháp cá thể:

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

Trong khi tôi thay đổi dữ liệu trong cây objectToClone, không có thay đổi trong cloneObject. Đó là sự kiện của tôi.

Hy vọng nó giúp


1
2017-10-22 09:01