Câu hỏi Cách hiệu quả nhất để sao chép sâu một đối tượng trong JavaScript là gì?


Cách hiệu quả nhất để sao chép một đối tượng JavaScript là gì? tôi đã nhìn thấy obj = eval(uneval(o)); đang được sử dụng, nhưng không chuẩn và chỉ được Firefox hỗ trợ.

 Tôi đã làm những việc như obj = JSON.parse(JSON.stringify(o)); nhưng đặt câu hỏi về hiệu quả.

 Tôi cũng đã thấy chức năng sao chép đệ quy với nhiều sai sót khác nhau.
Tôi ngạc nhiên không có giải pháp kinh điển nào tồn tại.


4548
2018-06-06 14:59


gốc


Đánh giá không phải là điều ác. Sử dụng eval kém là. Nếu bạn sợ các tác dụng phụ của nó, bạn đang sử dụng nó sai. Các tác dụng phụ bạn lo sợ là những lý do để sử dụng nó. Có ai thực sự trả lời câu hỏi của bạn không? - Prospero
Nhân bản các đối tượng là một doanh nghiệp phức tạp, đặc biệt là với các đối tượng tùy chỉnh của các bộ sưu tập tùy ý. Đó có lẽ là lý do tại sao không có cách nào để làm điều đó. - b01
eval() thường là một ý tưởng tồi vì nhiều trình tối ưu hóa của công cụ Javascript phải tắt khi xử lý các biến được đặt qua eval. Chỉ có eval() trong mã của bạn có thể dẫn đến hiệu suất kém hơn. - user568458
Có thể trùng lặp Cách thanh lịch nhất để sao chép một đối tượng JavaScript - John Slegers
đây là một so sánh hiệu suất giữa các loại đối tượng nhân bản phổ biến nhất: jsben.ch/#/t917Z - EscapeNetscape


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


Chú thích: Đây là câu trả lời cho câu trả lời khác, không phải là câu trả lời đúng cho câu hỏi này. Nếu bạn muốn có nhân bản đối tượng nhanh hãy làm theo Lời khuyên của Corban trong câu trả lời của họ cho câu hỏi này.


Tôi muốn lưu ý rằng .clone() phương pháp trong jQuery chỉ nhân bản các phần tử DOM. Để sao chép các đối tượng JavaScript, bạn sẽ làm:

// Shallow copy
var newObject = jQuery.extend({}, oldObject);

// Deep copy
var newObject = jQuery.extend(true, {}, oldObject);

Bạn có thể tìm thêm thông tin trong Tài liệu jQuery.

Tôi cũng muốn lưu ý rằng bản sao sâu thực sự thông minh hơn nhiều so với những gì được hiển thị ở trên - nó có thể tránh được nhiều bẫy (ví dụ cố gắng mở rộng sâu phần tử DOM). Nó được sử dụng thường xuyên trong lõi jQuery và trong các plugin để có hiệu quả tuyệt vời.


4067



Đối với những người không nhận ra, câu trả lời của John Resig có lẽ được dự định là một dạng phản ứng / giải thích cho Câu trả lời của ConroyP, thay vì trả lời trực tiếp cho câu hỏi. - S. Kirby
@ThiefMaster github.com/jquery/jquery/blob/master/src/core.js tại dòng 276 (có một chút mã mà làm điều gì đó khác nhưng mã cho "làm thế nào để làm điều này trong JS" là có :) - Rune FS
Đây là mã JS đằng sau bản sao sâu của jQuery, cho bất kỳ ai quan tâm: github.com/jquery/jquery/blob/master/src/core.js#L265-327 - Alex W
Woah! Chỉ cần siêu rõ ràng: không biết tại sao câu trả lời này được chọn làm câu trả lời đúng, đây là câu trả lời cho các câu trả lời được đưa ra dưới đây: stackoverflow.com/a/122190/6524(đã được giới thiệu .clone(), đó không phải là mã đúng để sử dụng trong ngữ cảnh này). Thật không may câu hỏi này đã trải qua rất nhiều sửa đổi mà cuộc thảo luận ban đầu không còn rõ ràng nữa! Hãy làm theo lời khuyên của Corban và viết một vòng lặp hoặc sao chép các thuộc tính trực tiếp qua một đối tượng mới, nếu bạn quan tâm đến tốc độ. Hoặc tự mình thử nghiệm! - John Resig
Làm thế nào một trong những sẽ làm điều này mà không cần sử dụng jQuery? - Awesomeness01


Thanh toán điểm chuẩn này: http://jsben.ch/#/bWfk9

Trong các bài kiểm tra trước đây của tôi, tốc độ là mối quan tâm chính tôi tìm thấy

JSON.parse(JSON.stringify(obj))

là cách nhanh nhất để sao chép sâu một đối tượng (nó đập ra jQuery.extend với cờ sâu được đặt đúng 10-20%).

jQuery.extend khá nhanh khi cờ sâu được đặt thành false (bản sao nông). Nó là một lựa chọn tốt, bởi vì nó bao gồm một số logic bổ sung để xác nhận loại và không sao chép trên các thuộc tính không xác định, vv, nhưng điều này cũng sẽ làm chậm bạn xuống một chút.

Nếu bạn biết cấu trúc của các đối tượng mà bạn đang cố gắng sao chép hoặc có thể tránh các mảng lồng nhau sâu, bạn có thể viết đơn giản for (var i in obj) loop để sao chép đối tượng của bạn trong khi kiểm tra hasOwnProperty và nó sẽ nhanh hơn nhiều so với jQuery.

Cuối cùng nếu bạn đang cố gắng sao chép một cấu trúc đối tượng đã biết trong một vòng lặp nóng, bạn có thể nhận được MUCH NHIỀU HIỆU SUẤT THÊM bằng cách đơn giản nằm trong thủ tục nhân bản và tự xây dựng đối tượng.

Công cụ theo dõi JavaScript hút tối ưu hóa for..in vòng lặp và kiểm tra hasOwnProperty sẽ làm chậm bạn xuống là tốt. Bản sao thủ công khi tốc độ là tuyệt đối phải.

var clonedObject = {
  knownProp: obj.knownProp,
  ..
}

Cẩn thận khi sử dụng JSON.parse(JSON.stringify(obj)) phương pháp trên Date các đối tượng - JSON.stringify(new Date()) trả về một biểu diễn chuỗi của ngày theo định dạng ISO, JSON.parse()  không chuyển đổi lại thành Date vật. Xem câu trả lời này để biết thêm chi tiết.

Ngoài ra, xin lưu ý rằng, trong Chrome 65 ít nhất, nhân bản bản địa không phải là con đường để đi. Theo JSPerf này, thực hiện nhân bản bản địa bằng cách tạo một hàm mới gần như 800x chậm hơn so với sử dụng JSON.stringify cực kỳ nhanh chóng trên toàn bộ bảng.


1877



@trysis Object.create không nhân bản đối tượng, đang sử dụng đối tượng nguyên mẫu ... jsfiddle.net/rahpuser/yufzc1jt/2 - rahpuser
Phương pháp này cũng sẽ xóa keys từ của bạn object, có functions như giá trị của họ, bởi vì JSON không hỗ trợ chức năng. - Karlen Kishmiryan
Cũng nên nhớ rằng sử dụng JSON.parse(JSON.stringify(obj)) vào Date Objects cũng sẽ chuyển đổi ngày trở lại UTC trong biểu diễn chuỗi trong ISO8601 định dạng. - dnlgmzddr
Cách tiếp cận JSON cũng cuộn cảm trên các tham chiếu vòng tròn. - rich remer
@velop, Object.assign ({}, objToClone) có vẻ như nó hiện một bản sao nông mặc dù - sử dụng nó trong khi chơi xung quanh trong giao diện điều khiển công cụ dev, đối tượng nhân bản vẫn trỏ đến một tham chiếu của đối tượng nhân bản vô tính. Vì vậy, tôi không nghĩ rằng nó thực sự áp dụng ở đây. - Garrett Simpson


Giả sử rằng bạn chỉ có các biến và không phải bất kỳ hàm nào trong đối tượng của bạn, bạn chỉ có thể sử dụng:

var newObject = JSON.parse(JSON.stringify(oldObject));

402



con của cách tiếp cận này như tôi vừa tìm thấy là nếu đối tượng của bạn có bất kỳ chức năng nào (tôi có getters & setters nội bộ) thì chúng bị mất khi bị xâu chuỗi .. Nếu đó là tất cả những gì bạn cần thì phương pháp này là tốt .. - Markive
@ Jason, Lý do tại sao phương pháp này là chậm hơn so với nông sao chép (trên một đối tượng sâu) là phương pháp này, theo định nghĩa, bản sao sâu. Nhưng kể từ khi JSON được triển khai bằng mã gốc (trong hầu hết các trình duyệt), điều này sẽ nhanh hơn đáng kể so với việc sử dụng bất kỳ giải pháp sao chép sâu dựa trên javascript nào khác và có thể đôi khi nhanh hơn một kỹ thuật sao chép nông dựa trên javascript (xem: jsperf.com/cloning-an-object/79). - MiJyn
JSON.stringify({key: undefined}) //=> "{}" - Web_Designer
kỹ thuật này sẽ tiêu diệt tất cả Date các đối tượng được lưu trữ bên trong đối tượng, chuyển đổi chúng thành dạng chuỗi. - fstab
Nó sẽ không sao chép bất cứ thứ gì không phải là một phần của thông số JSON (json.org) - cdmckay


Cấu trúc nhân bản

HTML5 định nghĩa một thuật toán nhân bản "có cấu trúc" nội bộ có thể tạo ra các dòng vô tính của các đối tượng. Nó vẫn được giới hạn trong một số kiểu tích hợp nhất định, nhưng ngoài một số kiểu được hỗ trợ bởi JSON, nó cũng hỗ trợ Ngày tháng, RegExps, Bản đồ, Bộ, Blobs, FileLists, ImageDatas, mảng thưa thớt, Mảng đã nhậpvà có thể nhiều hơn nữa trong tương lai. Nó cũng bảo tồn các tham chiếu trong dữ liệu nhân bản, cho phép nó hỗ trợ các cấu trúc theo chu kỳ và đệ quy có thể gây ra lỗi cho JSON.

Hỗ trợ trực tiếp trong trình duyệt: Sắp có?

Trình duyệt hiện không cung cấp giao diện trực tiếp cho thuật toán nhân bản có cấu trúc, nhưng structuredClone() chức năng đang được tích cực thảo luận trong whatwg / html # 793 trên GitHub và có thể sẽ sớm ra mắt! Như hiện đang được đề xuất, việc sử dụng nó cho hầu hết các mục đích sẽ đơn giản như:

const clone = structuredClone(original);

Cho đến khi điều này được vận chuyển, việc triển khai bản sao có cấu trúc của trình duyệt chỉ được hiển thị gián tiếp.

Giải pháp không đồng bộ: Có thể sử dụng.

Cách thấp hơn để tạo một bản sao có cấu trúc với các API hiện có là đăng dữ liệu qua một cổng của một MessageChannels. Cổng khác sẽ phát ra một message sự kiện với bản sao có cấu trúc của phần đính kèm .data. Thật không may, lắng nghe những sự kiện này nhất thiết là không đồng bộ, và các lựa chọn thay thế đồng bộ ít thực tế hơn.

class StructuredCloner {
  constructor() {
    this.pendingClones_ = new Map();
    this.nextKey_ = 0;

    const channel = new MessageChannel();
    this.inPort_ = channel.port1;
    this.outPort_ = channel.port2;

    this.outPort_.onmessage = ({data: {key, value}}) => {
      const resolve = this.pendingClones_.get(key);
      resolve(value);
      this.pendingClones_.delete(key);
    };
    this.outPort_.start();
  }

  cloneAsync(value) {
    return new Promise(resolve => {
      const key = this.nextKey_++;
      this.pendingClones_.set(key, resolve);
      this.inPort_.postMessage({key, value});
    });
  }
}

const structuredCloneAsync = window.structuredCloneAsync =
    StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner);

Ví dụ sử dụng:

const main = async () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = await structuredCloneAsync(original);

  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));

  console.log("Assertions complete.");
};

main();

Giải pháp đồng bộ: Awful!

Không có lựa chọn tốt cho việc tạo các bản sao có cấu trúc đồng bộ. Dưới đây là một vài thay đổi không thực tế.

history.pushState() và history.replaceState() cả hai tạo ra một bản sao có cấu trúc của đối số đầu tiên của họ và gán giá trị đó cho history.state. Bạn có thể sử dụng điều này để tạo một bản sao có cấu trúc của bất kỳ đối tượng nào như thế này:

const structuredClone = obj => {
  const oldState = history.state;
  history.replaceState(obj, null);
  const clonedObj = history.state;
  history.replaceState(oldState, null);
  return clonedObj;
};

Ví dụ sử dụng:

'use strict';

const main = () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = structuredClone(original);
  
  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));
  
  console.log("Assertions complete.");
};

const structuredClone = obj => {
  const oldState = history.state;
  history.replaceState(obj, null);
  const clonedObj = history.state;
  history.replaceState(oldState, null);
  return clonedObj;
};

main();

Mặc dù đồng bộ, điều này có thể cực kỳ chậm. Nó phát sinh tất cả các chi phí liên quan đến thao tác lịch sử trình duyệt. Việc gọi phương thức này nhiều lần có thể khiến Chrome trở nên tạm thời không hồi đáp.

Các Notification constructor tạo ra một bản sao có cấu trúc của dữ liệu liên quan của nó. Nó cũng cố gắng để hiển thị một thông báo trình duyệt cho người dùng, nhưng điều này sẽ âm thầm thất bại trừ khi bạn đã yêu cầu quyền thông báo. Trong trường hợp bạn có sự cho phép cho các mục đích khác, chúng tôi sẽ ngay lập tức đóng thông báo mà chúng tôi đã tạo.

const structuredClone = obj => {
  const n = new Notification('', {data: obj, silent: true});
  n.onshow = n.close.bind(n);
  return n.data;
};

Ví dụ sử dụng:

'use strict';

const main = () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = structuredClone(original);
  
  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));
  
  console.log("Assertions complete.");
};

const structuredClone = obj => {
  const n = new Notification('', {data: obj, silent: true});
  n.close();
  return n.data;
};

main();


274



@rynah Tôi chỉ xem xét thông số kỹ thuật một lần nữa và bạn nói đúng: history.pushState() và history.replaceState() cả hai phương thức đều được đặt đồng bộ history.state cho một bản sao có cấu trúc của đối số đầu tiên của họ. Một chút lạ, nhưng nó hoạt động. Tôi đang cập nhật câu trả lời của tôi ngay bây giờ. - Jeremy
Điều này chỉ là sai! API đó không có nghĩa là được sử dụng theo cách này. - Fardin
Là người đã thực hiện pushState trong Firefox, tôi cảm thấy một sự pha trộn kỳ lạ của niềm kiêu hãnh và sự quấy rầy ở bản hack này. Làm tốt lắm, các bạn. - Justin L.


Nếu không có bất kỳ nội trang dựng sẵn nào, bạn có thể thử:

    function clone(obj) {
      if (obj === null || typeof(obj) !== 'object' || 'isActiveClone' in obj)
        return obj;

      if (obj instanceof Date)
        var temp = new obj.constructor(); //or new Date(obj);
      else
        var temp = obj.constructor();

      for (var key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
          obj['isActiveClone'] = null;
          temp[key] = clone(obj[key]);
          delete obj['isActiveClone'];
        }
      }

      return temp;
    }


273



Giải pháp JQuery sẽ làm việc cho các phần tử DOM nhưng không chỉ đối với bất kỳ đối tượng nào. Mootools có cùng giới hạn. Chúc họ có một "bản sao" chung cho bất kỳ đối tượng nào ... Các giải pháp đệ quy nên làm việc cho bất cứ điều gì. Đó có lẽ là con đường để đi. - jschrab
Hàm này ngắt nếu đối tượng được nhân bản có một hàm tạo yêu cầu các tham số. Có vẻ như chúng ta có thể thay đổi nó thành "var temp = new Object ()" và nó có hoạt động trong mọi trường hợp không? - Andrew Arnott
Andrew, nếu bạn thay đổi nó thành var temp = new Object (), thì bản sao của bạn sẽ không có nguyên mẫu giống như đối tượng gốc. Hãy thử sử dụng: 'var newProto = function () {}; newProto.prototype = obj.constructor; var temp = new newProto (); ' - limscoder
Tương tự như câu trả lời của trình quản lý chi tiết, xem câu trả lời của tôi bên dưới về cách thực hiện điều này mà không cần gọi hàm tạo: stackoverflow.com/a/13333781/560114 - Matt Browne
Đối với các đối tượng có chứa tham chiếu đến các phần con (tức là, mạng của đối tượng), điều này không hoạt động: Nếu hai tham chiếu trỏ đến cùng một đối tượng phụ, bản sao chứa hai bản sao khác nhau của nó. Và nếu có các tham chiếu đệ quy, hàm sẽ không bao giờ chấm dứt (tốt, ít nhất không theo cách bạn muốn :-) Đối với các trường hợp chung này, bạn phải thêm từ điển của các đối tượng đã được sao chép và kiểm tra xem bạn đã sao chép chưa ... Lập trình rất phức tạp khi bạn sử dụng một ngôn ngữ đơn giản - virtualnobi


Cách hiệu quả để sao chép (không phải bản sao sâu) một đối tượng trong một dòng mã

An Object.assign là một phần của tiêu chuẩn ECMAScript 2015 (ES6) và thực hiện chính xác những gì bạn cần.

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

Phương thức Object.assign () được sử dụng để sao chép các giá trị của tất cả các thuộc tính riêng có thể đếm được từ một hoặc nhiều đối tượng nguồn đến một đối tượng đích.

Đọc thêm...

Các polyfill để hỗ trợ các trình duyệt cũ hơn:

if (!Object.assign) {
  Object.defineProperty(Object, 'assign', {
    enumerable: false,
    configurable: true,
    writable: true,
    value: function(target) {
      'use strict';
      if (target === undefined || target === null) {
        throw new TypeError('Cannot convert first argument to object');
      }

      var to = Object(target);
      for (var i = 1; i < arguments.length; i++) {
        var nextSource = arguments[i];
        if (nextSource === undefined || nextSource === null) {
          continue;
        }
        nextSource = Object(nextSource);

        var keysArray = Object.keys(nextSource);
        for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
          var nextKey = keysArray[nextIndex];
          var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
          if (desc !== undefined && desc.enumerable) {
            to[nextKey] = nextSource[nextKey];
          }
        }
      }
      return to;
    }
  });
}

132



Điều này không sao chép đệ quy vì vậy không thực sự cung cấp một giải pháp cho vấn đề nhân bản một đối tượng. - mwhite
Phương pháp này làm việc, mặc dù tôi đã thử nghiệm một vài và _.extend ({}, (obj)) là BY FAR nhanh nhất: nhanh hơn 20 lần so với JSON.parse và nhanh hơn 60% so với Object.assign. Nó sao chép tất cả các đối tượng phụ khá tốt. - Nico
@mwhite có sự khác biệt giữa bản sao và bản sao sâu. Câu trả lời này trong thực tế nhân bản, nhưng nó không sao chép sâu. - Meirion Hughes
các op yêu cầu cho bản sao sâu. điều này không làm bản sao sâu. - user566245
wow rất nhiều phản ứng Object.assign mà không cần đọc câu hỏi của op ... - martin8768


Mã số:

// extends 'from' object with members from 'to'. If 'to' is null, a deep clone of 'from' is returned
function extend(from, to)
{
    if (from == null || typeof from != "object") return from;
    if (from.constructor != Object && from.constructor != Array) return from;
    if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function ||
        from.constructor == String || from.constructor == Number || from.constructor == Boolean)
        return new from.constructor(from);

    to = to || new from.constructor();

    for (var name in from)
    {
        to[name] = typeof to[name] == "undefined" ? extend(from[name], null) : to[name];
    }

    return to;
}

Kiểm tra:

var obj =
{
    date: new Date(),
    func: function(q) { return 1 + q; },
    num: 123,
    text: "asdasd",
    array: [1, "asd"],
    regex: new RegExp(/aaa/i),
    subobj:
    {
        num: 234,
        text: "asdsaD"
    }
}

var clone = extend(obj);

91



Thế còn var obj = {} và obj.a = obj - neaumusic
Tôi không hiểu chức năng này. Giả sử from.constructor Là Date ví dụ. Làm thế nào thứ ba if kiểm tra đạt được khi lần thứ 2 if kiểm tra sẽ thành công và làm cho hàm trả về (vì Date != Object && Date != Array)? - Adam McKee
@AdamMcKee Bởi vì javascript đối số đi qua và chuyển nhượng biến là khó khăn. Cách tiếp cận này hoạt động tốt, bao gồm cả ngày (mà thực sự được xử lý bởi các bài kiểm tra thứ hai) - fiddle để kiểm tra ở đây: jsfiddle.net/zqv9q9c6. - brichins
* Tôi có nghĩa là "tham số phân công" (trong một cơ quan chức năng), không phải "chuyển nhượng biến". Mặc dù nó giúp hiểu rõ cả hai. - brichins
@NickSweeting: Thử - có thể nó hoạt động. Nếu không - sửa chữa và cập nhật câu trả lời. Đó là cách nó hoạt động ở đây trong cộng đồng :) - Kamarey


Đây là những gì tôi đang sử dụng:

function cloneObject(obj) {
    var clone = {};
    for(var i in obj) {
        if(typeof(obj[i])=="object" && obj[i] != null)
            clone[i] = cloneObject(obj[i]);
        else
            clone[i] = obj[i];
    }
    return clone;
}

81



Điều này có vẻ không đúng. cloneObject({ name: null }) => {"name":{}} - Niyaz
Điều này là do một điều ngu ngốc khác trong javascript typeof null > "object" nhưng Object.keys(null) > TypeError: Requested keys of a value that is not an object. thay đổi điều kiện thành if(typeof(obj[i])=="object" && obj[i]!=null) - Vitim.us
Điều này sẽ gán các thuộc tính đếm được kế thừa của obj trực tiếp vào bản sao và giả định rằng obj là một đối tượng đơn giản. - RobG
Điều này cũng làm hỏng mảng, được chuyển đổi thành các đối tượng bằng các phím số. - blade
Không phải là một vấn đề nếu bạn không sử dụng null. - Jorge Bucaran


Bản sao sâu theo hiệu suất: Xếp hạng từ tốt nhất đến tồi tệ nhất

  • Chỉ định lại "=" (mảng chuỗi, mảng số - chỉ)
  • Slice (mảng chuỗi, mảng số - chỉ)
  • Ghép nối (mảng chuỗi, mảng số - chỉ)
  • Chức năng tùy chỉnh: cho vòng lặp hoặc đệ quy sao chép
  • jQuery's $ .extend
  • JSON.parse (mảng chuỗi, mảng số, mảng đối tượng - chỉ)
  • Underscore.js's _.clone (mảng chuỗi, mảng số - chỉ)
  • Lo-Dash's _.cloneDeep

Sao chép sâu một mảng các chuỗi hoặc số (một cấp - không có con trỏ tham chiếu):

Khi một mảng chứa các số và chuỗi - các hàm như .slice (), .concat (), .splice (), toán tử gán "=" và hàm nhân bản của Underscore.js; sẽ tạo bản sao sâu của các phần tử của mảng.

Nơi phân công lại có hiệu suất nhanh nhất:

var arr1 = ['a', 'b', 'c'];
var arr2 = arr1;
arr1 = ['a', 'b', 'c'];

Và .slice () có hiệu suất tốt hơn so với .concat (), http://jsperf.com/duplicate-array-slice-vs-concat/3

var arr1 = ['a', 'b', 'c'];  // Becomes arr1 = ['a', 'b', 'c']
var arr2a = arr1.slice(0);   // Becomes arr2a = ['a', 'b', 'c'] - deep copy
var arr2b = arr1.concat();   // Becomes arr2b = ['a', 'b', 'c'] - deep copy

Sao chép sâu một mảng các đối tượng (hai hoặc nhiều cấp độ - các con trỏ tham chiếu):

var arr1 = [{object:'a'}, {object:'b'}];

Viết một hàm tùy chỉnh (có hiệu suất nhanh hơn $ .extend () hoặc JSON.parse):

function copy(o) {
   var out, v, key;
   out = Array.isArray(o) ? [] : {};
   for (key in o) {
       v = o[key];
       out[key] = (typeof v === "object" && v !== null) ? copy(v) : v;
   }
   return out;
}

copy(arr1);

Sử dụng các chức năng tiện ích của bên thứ ba:

$.extend(true, [], arr1); // Jquery Extend
JSON.parse(arr1);
_.cloneDeep(arr1); // Lo-dash

Trường hợp $ .extend của jQuery có hiệu suất tốt hơn:


64



Tôi đã thử nghiệm một vài và _.extend ({}, (obj)) là BY FAR nhanh nhất: nhanh hơn 20 lần so với JSON.parse và nhanh hơn 60% so với Object.assign. Nó sao chép tất cả các đối tượng phụ khá tốt. - Nico
@NicoDurand - Các bài kiểm tra hiệu suất của bạn có trực tuyến không? - tfmontague
Tất cả các ví dụ của bạn là nông cạn, một cấp độ. Đây không phải là câu trả lời hay. Câu hỏi liên quan đến sâu nhân bản tức là ít nhất hai cấp độ. - Karl Morrison
Một bản sao sâu là khi một đối tượng được sao chép trong toàn bộ của nó mà không cần sử dụng các con trỏ tham chiếu đến các đối tượng khác. Các kỹ thuật trong phần "Sao chép sâu một mảng các đối tượng", chẳng hạn như jQuery.extend () và chức năng tùy chỉnh (là đệ quy) sao chép các đối tượng với "ít nhất hai cấp". Vì vậy, không phải tất cả các ví dụ đều là các bản sao "một cấp". - tfmontague
Tôi thích chức năng sao chép tùy chỉnh của bạn, nhưng bạn nên loại trừ các giá trị null, nếu không tất cả các giá trị null sẽ được chuyển đổi thành các đối tượng, tức là: out[key] = (typeof v === "object" && v !== null) ? copy(v) : v; - josi


var clone = function() {
    var newObj = (this instanceof Array) ? [] : {};
    for (var i in this) {
        if (this[i] && typeof this[i] == "object") {
            newObj[i] = this[i].clone();
        }
        else
        {
            newObj[i] = this[i];
        }
    }
    return newObj;
}; 

Object.defineProperty( Object.prototype, "clone", {value: clone, enumerable: false});

56





Tôi biết đây là một bài đăng cũ, nhưng tôi nghĩ rằng đây có thể là một số trợ giúp cho người tiếp theo tình cờ gặp.

Miễn là bạn không gán một đối tượng cho bất kỳ thứ gì, nó không duy trì tham chiếu trong bộ nhớ. Vì vậy, để tạo một đối tượng mà bạn muốn chia sẻ giữa các đối tượng khác, bạn sẽ phải tạo một nhà máy như sau:

var a = function(){
    return {
        father:'zacharias'
    };
},
b = a(),
c = a();
c.father = 'johndoe';
alert(b.father);

49



Câu trả lời này không thực sự có liên quan bởi vì câu hỏi là: ví dụ b làm thế nào để tạo ra một bản sao c WHILE không biết về nhà máy một hoặc không muốn sử dụng nhà máy a. Lý do người ta có thể không muốn sử dụng nhà máy là sau khi instantiation b có thể đã được khởi tạo với dữ liệu bổ sung (ví dụ: đầu vào của người dùng). - Noel Abrahams
Đúng là đây không thực sự là câu trả lời cho câu hỏi, nhưng tôi nghĩ điều quan trọng là ở đây vì đây là câu trả lời cho câu hỏi mà tôi nghi ngờ nhiều người đến đây thực sự có ý nghĩa để hỏi. - Semicolon
Xin lỗi các bạn, tôi thực sự không hiểu tại sao có quá nhiều upvotes. Nhân bản một đối tượng là một khái niệm khá rõ ràng, bạn hình nón một đối tượng từ một đối tượng KHÁC, và nó không có nhiều việc phải làm với việc tạo một đối tượng mới với mẫu nhà máy. - opensas
Trong khi điều này làm việc cho các đối tượng được xác định trước, "nhân bản" theo cách này sẽ không nhận ra các thuộc tính mới được thêm vào đối tượng ban đầu. Nếu bạn tạo, thêm thuộc tính mới vào a, sau đó tạo b. b sẽ không có tài sản mới. Về cơ bản, mô hình nhà máy là bất biến đối với các thuộc tính mới. Đây không phải là nhân bản theo mô hình. Xem: jsfiddle.net/jzumbrun/42xejnbx - Jon
Tôi nghĩ đây là lời khuyên tốt, nói chung, thay vì sử dụng const defaultFoo = { a: { b: 123 } }; bạn có thể đi const defaultFoo = () => ({ a: { b: 123 } }; và vấn đề của bạn được giải quyết. Tuy nhiên, nó thực sự không phải là câu trả lời cho câu hỏi. Nó có thể có ý nghĩa hơn như một bình luận về câu hỏi, không phải là một câu trả lời đầy đủ. - Josh from Qaribou