tra-loi-cau-hoi-phat-trien-web.com

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 đã thấy obj = eval(uneval(o)); đang được sử dụng, nhưng đó là 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 các chức năng sao chép đệ quy với các lỗi 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.

4863
jschrab

Lưu ý: Đâ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 thích hợp cho câu hỏi này. Nếu bạn muốn 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 phương thức .clone() trong jQuery chỉ sao chép 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);

Thông tin thêm có thể được tìm thấy 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 - ví dụ, nó có thể tránh được nhiều bẫy (cố gắng mở rộng sâu một phần tử DOM). Nó được sử dụng thường xuyên trong lõi jQuery và các plugin để có hiệu quả tuyệt vời.

4217
John Resig

Kiểm tra điểm chuẩn này: http://jsben.ch/#/bWfk9

Trong các thử nghiệm trước đây của tôi, nơi tốc độ là mối quan tâm chính mà 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ó vượt qua 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). Đây là một tùy chọn tốt, vì nó bao gồm một số logic bổ sung để xác thực loại và không sao chép các thuộc tính không xác định, v.v., nhưng điều này cũng sẽ làm bạn chậm lại một chút.

Nếu bạn biết cấu trúc của các đối tượng bạn đang cố sao chép hoặc có thể tránh các mảng lồng nhau sâu, bạn có thể viết một vòng lặp for (var i in obj) đơn giản để 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 NHIỀU HIỆU SUẤT HƠN bằng cách đơn giản là lót thủ tục nhân bản và xây dựng đối tượng theo cách thủ công.

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

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

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

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

2066
Corban Brook

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

var newObject = JSON.parse(JSON.stringify(oldObject));
434
Sultan Shakir

Nhân bản cấu trúc

Tiêu chuẩn HTML bao gồm thuật toán nhân bản/tuần tự hóa có cấu trúc bên trong có thể tạo các bản sao sâu của các đối tượng. Nó vẫn bị giới hạn ở một số loại tích hợp nhất định, nhưng ngoài một số loại được JSON hỗ trợ, nó còn hỗ trợ Dates, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, thưa thớt, Mảng gõ và có thể nhiều hơn trong tương lai . Nó cũng bảo tồn các tham chiếu trong dữ liệu được sao chép, cho phép nó hỗ trợ các cấu trúc đệ quy và đệ quy có thể gây ra lỗi cho JSON.

Hỗ trợ trong Node.js: Thử nghiệm ????

Mô-đun v8 trong Node.js hiện tại (kể từ Nút 11) hiển thị trực tiếp API tuần tự hóa có cấu trúc , nhưng chức năng này vẫn được đánh dấu là "thử nghiệm" và có thể thay đổi hoặc xóa trong các phiên bản trong tương lai. Nếu bạn đang sử dụng một phiên bản tương thích, việc nhân bản một đối tượng cũng đơn giản như:

const v8 = require('v8');

const structuredClone = obj => {
  return v8.deserialize(v8.serialize(obj));
};

Hỗ trợ trực tiếp trong trình duyệt: Có thể cuối cùng? ????

Cá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 hàm structuredClone() toàn cầu đã được thảo luận trong whatwg/html # 793 trên GitHub . Như hiện tại đề xuất, sử dụng nó cho hầu hết các mục đích sẽ đơn giản như:

const clone = structuredClone(original);

Trừ 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 phơi bày gián tiếp.

Cách giải quyết không đồng bộ: Có thể sử dụng. ????

Cách chi phí thấp hơn để tạo 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 MessageChannels . Cổng còn lại sẽ phát ra một sự kiện message với một bản sao có cấu trúc của .data đính kèm. 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ộ: Tuyệt vời! ????

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

Cả history.pushState()history.replaceState() đều tạo một bản sao có cấu trúc của đối số đầu tiên của chúng 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ải chịu tất cả các chi phí liên quan đến thao túng lịch sử trình duyệt. Gọi phương thức này nhiều lần có thể khiến Chrome tạm thời không phản hồi.

Hàm tạo Notification tạo một bản sao có cấu trúc của dữ liệu liên quan. Nó cũng cố gắng hiển thị 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ó quyền cho các mục đích khác, chúng tôi sẽ ngay lập tức đóng thông báo 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();

308
Jeremy Banks

Nếu không có bất kỳ nội dung 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;
}
295
ConroyP

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ã

Phương thức 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 vào một đối tượng đích.

Đọc thêm...

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;
    }
  });
}
149
Eugene Tiurin

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);
94
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;
}
86
Alan

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

  • Tái chỉ định "=" (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ỉ)
  • Hàm tùy chỉnh: for-loop hoặc bản sao đệ quy
  • $ .extend của jQuery
  • 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ỉ)
  • _ -CloneDeep của Lo-Dash

Sao chép sâu một mảng 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 clone của Underscore.js; sẽ tạo một bản sao sâu của các phần tử của mảng.

Trường hợp tái chỉ định 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 .concat (), http://jsperf.com/d repeatate-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 - con trỏ tham chiếu):

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

Viế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

Trong đó $ .extend của jQuery có hiệu suất tốt hơn:

73
tfmontague
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});
60
Zibri

Tôi biết đây là một bài viết 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 phải.

Miễn là bạn không gán một đối tượng cho bất cứ 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ư vậy:

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

Cloning Một đối tượng luôn là mối quan tâm trong JS, nhưng đó là tất cả về trước ES6, tôi liệt kê các cách khác nhau để sao chép một đối tượng trong JavaScript bên dưới, hãy tưởng tượng bạn có Object bên dưới và muốn có một bản sao sâu về điều đó:

var obj = {a:1, b:2, c:3, d:4};

Có một số cách để sao chép đối tượng này mà không thay đổi Nguồn gốc:

1) ES5 +, Sử dụng chức năng đơn giản để thực hiện sao chép cho bạn:

function deepCopyObj(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    if (obj instanceof Date) {
        var copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }
    if (obj instanceof Array) {
        var copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = cloneSO(obj[i]);
        }
        return copy;
    }
    if (obj instanceof Object) {
        var copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = cloneSO(obj[attr]);
        }
        return copy;
    }
    throw new Error("Unable to copy obj this object.");
}

2) ES5 +, sử dụng JSON.parse và JSON.opesify.

var  deepCopyObj = JSON.parse(JSON.stringify(obj));

3) AngularJs: 

var  deepCopyObj = angular.copy(obj);

4) jQuery: 

var deepCopyObj = jQuery.extend(true, {}, obj);

5) UnderscoreJs & Loadash: 

var deepCopyObj = _.cloneDeep(obj); //latest version UndescoreJs makes shallow copy

Hy vọng những sự giúp đỡ này ... 

52
Alireza

Có thư viện a (được gọi là bản sao clone) , thực hiện điều này khá tốt. Nó cung cấp bản sao đệ quy/sao chép hoàn chỉnh nhất của các đối tượng tùy ý mà tôi biết. Nó cũng hỗ trợ các tài liệu tham khảo tròn, chưa được bao gồm trong các câu trả lời khác.

Bạn có thể tìm thấy nó vào npm , quá. Nó có thể được sử dụng cho trình duyệt cũng như Node.js.

Dưới đây là một ví dụ về cách sử dụng nó:

Cài đặt nó với

npm install clone

hoặc gói nó với Ender .

ender build clone [...]

Bạn cũng có thể tải mã nguồn theo cách thủ công.

Sau đó, bạn có thể sử dụng nó trong mã nguồn của bạn.

var clone = require('clone');

var a = { foo: { bar: 'baz' } };  // inital value of a
var b = clone(a);                 // clone a -> b
a.foo.bar = 'foo';                // change a

console.log(a);                   // { foo: { bar: 'foo' } }
console.log(b);                   // { foo: { bar: 'baz' } }

(Tuyên bố miễn trừ trách nhiệm: Tôi là tác giả của thư viện.)

52
pvorb

Nếu bạn đang sử dụng nó, thư viện Underscore.js có phương thức clone .

var newObject = _.clone(oldObject);
48
itsadok

Đây là phiên bản câu trả lời của ConroyP ở trên, hoạt động ngay cả khi hàm tạo có các tham số bắt buộc:

//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
    object_create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

function deepCopy(obj) {
    if(obj == null || typeof(obj) !== 'object'){
        return obj;
    }
    //make sure the returned object has the same prototype as the original
    var ret = object_create(obj.constructor.prototype);
    for(var key in obj){
        ret[key] = deepCopy(obj[key]);
    }
    return ret;
}

Chức năng này cũng có sẵn trong thư viện Simpleoo của tôi.

Chỉnh sửa:

Đây là phiên bản mạnh mẽ hơn (nhờ Justin McCandless, giờ đây cũng hỗ trợ các tài liệu tham khảo theo chu kỳ):

/**
 * Deep copy an object (make copies of all its object properties, sub-properties, etc.)
 * An improved version of http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
 * that doesn't break if the constructor has required parameters
 * 
 * It also borrows some code from http://stackoverflow.com/a/11621004/560114
 */ 
function deepCopy(src, /* INTERNAL */ _visited, _copiesVisited) {
    if(src === null || typeof(src) !== 'object'){
        return src;
    }

    //Honor native/custom clone methods
    if(typeof src.clone == 'function'){
        return src.clone(true);
    }

    //Special cases:
    //Date
    if(src instanceof Date){
        return new Date(src.getTime());
    }
    //RegExp
    if(src instanceof RegExp){
        return new RegExp(src);
    }
    //DOM Element
    if(src.nodeType && typeof src.cloneNode == 'function'){
        return src.cloneNode(true);
    }

    // Initialize the visited objects arrays if needed.
    // This is used to detect cyclic references.
    if (_visited === undefined){
        _visited = [];
        _copiesVisited = [];
    }

    // Check if this object has already been visited
    var i, len = _visited.length;
    for (i = 0; i < len; i++) {
        // If so, get the copy we already made
        if (src === _visited[i]) {
            return _copiesVisited[i];
        }
    }

    //Array
    if (Object.prototype.toString.call(src) == '[object Array]') {
        //[].slice() by itself would soft clone
        var ret = src.slice();

        //add it to the visited array
        _visited.Push(src);
        _copiesVisited.Push(ret);

        var i = ret.length;
        while (i--) {
            ret[i] = deepCopy(ret[i], _visited, _copiesVisited);
        }
        return ret;
    }

    //If we've reached here, we have a regular object

    //make sure the returned object has the same prototype as the original
    var proto = (Object.getPrototypeOf ? Object.getPrototypeOf(src): src.__proto__);
    if (!proto) {
        proto = src.constructor.prototype; //this line would probably only be reached by very old browsers 
    }
    var dest = object_create(proto);

    //add this object to the visited array
    _visited.Push(src);
    _copiesVisited.Push(dest);

    for (var key in src) {
        //Note: this does NOT preserve ES5 property attributes like 'writable', 'enumerable', etc.
        //For an example of how this could be modified to do so, see the singleMixin() function
        dest[key] = deepCopy(src[key], _visited, _copiesVisited);
    }
    return dest;
}

//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
    object_create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}
36
Matt Browne

Sau đây tạo ra hai trường hợp của cùng một đối tượng. Tôi tìm thấy nó và hiện đang sử dụng nó. Nó đơn giản và dễ sử dụng.

var objToCreate = JSON.parse(JSON.stringify(cloneThis));
31
nathan rogers

Các đối tượng sao chép sâu trong JavaScript (Tôi nghĩ là tốt nhất và đơn giản nhất)

1. Sử dụng JSON.parse (JSON.opesify (object));

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}
var newObj = JSON.parse(JSON.stringify(obj));
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

2.Sử dụng phương thức đã tạo

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

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}
var newObj = cloneObject(obj);
obj.b.c = 20;

console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

3. Sử dụng liên kết _.cloneDeep của Lo-Dash lodash

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

4. Sử dụng phương thức Object.assign ()

var obj = { 
  a: 1,
  b: 2
}

var newObj = _.clone(obj);
obj.b = 20;
console.log(obj); // { a: 1, b: 20 }
console.log(newObj); // { a: 1, b: 2 }  

NHƯNG SAU KHI

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = Object.assign({}, obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
// Note: Properties on the prototype chain and non-enumerable properties cannot be copied.

5. Sử dụng liên kết Underscore.js _.clone _ ​​ Underscore.js

var obj = { 
  a: 1,
  b: 2
}

var newObj = _.clone(obj);
obj.b = 20;
console.log(obj); // { a: 1, b: 20 }
console.log(newObj); // { a: 1, b: 2 }  

NHƯNG SAU KHI

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
// (Create a shallow-copied clone of the provided plain object. Any nested objects or arrays will be copied by reference, not duplicated.)

Phương tiện tham khảo.com

JSBEN.CH Sân chơi điểm chuẩn hiệu suất 1 ~ 3 http://jsben.ch/KVQLdPerformance Deep copying objects in JavaScript

28
TinhNQ

Lodash có phương thức Nice _.cloneDeep (value) :

var objects = [{ 'a': 1 }, { 'b': 2 }];

var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
23
opensas

Crockford gợi ý (và tôi thích) sử dụng chức năng này:

function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

var newObject = object(oldObject);

Thật ngắn gọn, hoạt động như mong đợi và bạn không cần một thư viện.


CHỈNH SỬA:

Đây là một polyfill cho Object.create, vì vậy bạn cũng có thể sử dụng cái này.

var newObject = Object.create(oldObject);

LƯU Ý: Nếu bạn sử dụng một số thứ này, bạn có thể gặp vấn đề với một số lần lặp sử dụng hasOwnProperty. Bởi vì, create tạo đối tượng trống mới, người kế thừa oldObject. Nhưng nó vẫn hữu ích và thiết thực cho việc nhân bản các đối tượng.

Ví dụ nếu oldObject.a = 5;

newObject.a; // is 5

nhưng:

oldObject.hasOwnProperty(a); // is true
newObject.hasOwnProperty(a); // is false
23
Chris Broski
function clone(obj)
 { var clone = {};
   clone.prototype = obj.prototype;
   for (property in obj) clone[property] = obj[property];
   return clone;
 }
22
Mark Cidade

Shallow sao chép một lớp ( ECMAScript phiên bản 5 ):

var Origin = { foo : {} };
var copy = Object.keys(Origin).reduce(function(c,k){c[k]=Origin[k];return c;},{});

console.log(Origin, copy);
console.log(Origin == copy); // false
console.log(Origin.foo == copy.foo); // true

Và bản sao một lớp lót ( ECMAScript phiên bản 6 , 2015):

var Origin = { foo : {} };
var copy = Object.assign({}, Origin);

console.log(Origin, copy);
console.log(Origin == copy); // false
console.log(Origin.foo == copy.foo); // true
20
Maël Nison

Chỉ vì tôi đã không nhìn thấy AngularJS đã đề cập và nghĩ rằng mọi người có thể muốn biết ...

angular.copy cũng cung cấp phương thức sao chép sâu các đối tượng và mảng.

17
Dan Atkinson

Dường như không có toán tử nhân bản sâu lý tưởng nào cho các đối tượng giống như mảng. Như đoạn mã dưới đây minh họa, trình sao chép jQuery của John Resig biến các mảng có thuộc tính không phải là số thành các đối tượng không phải là mảng và trình sao chép JSON của RegDwight sẽ loại bỏ các thuộc tính không phải là số. Các thử nghiệm sau đây minh họa những điểm này trên nhiều trình duyệt:

function jQueryClone(obj) {
   return jQuery.extend(true, {}, obj)
}

function JSONClone(obj) {
   return JSON.parse(JSON.stringify(obj))
}

var arrayLikeObj = [[1, "a", "b"], [2, "b", "a"]];
arrayLikeObj.names = ["m", "n", "o"];
var JSONCopy = JSONClone(arrayLikeObj);
var jQueryCopy = jQueryClone(arrayLikeObj);

alert("Is arrayLikeObj an array instance?" + (arrayLikeObj instanceof Array) +
      "\nIs the jQueryClone an array instance? " + (jQueryCopy instanceof Array) +
      "\nWhat are the arrayLikeObj names? " + arrayLikeObj.names +
      "\nAnd what are the JSONClone names? " + JSONCopy.names)
16
Page Notes

Tôi có hai câu trả lời tốt tùy thuộc vào mục tiêu của bạn là sao chép "đối tượng JavaScript cũ đơn giản" hay không.

Chúng ta cũng giả sử rằng ý định của bạn là tạo một bản sao hoàn chỉnh mà không có tham chiếu nguyên mẫu nào quay lại đối tượng nguồn. Nếu bạn không quan tâm đến một bản sao hoàn chỉnh, thì bạn có thể sử dụng nhiều thói quen Object.clone () được cung cấp trong một số câu trả lời khác (mẫu của Crockford).

Đối với các đối tượng JavaScript cũ đơn giản, một cách tốt và cố gắng để sao chép một đối tượng trong thời gian chạy hiện đại khá đơn giản:

var clone = JSON.parse(JSON.stringify(obj));

Lưu ý rằng đối tượng nguồn phải là một đối tượng JSON thuần túy. Điều này có nghĩa là, tất cả các thuộc tính lồng nhau của nó phải là vô hướng (như boolean, chuỗi, mảng, đối tượng, v.v.). Bất kỳ chức năng hoặc đối tượng đặc biệt như RegExp hoặc Date sẽ không được sao chép.

Có hiệu quả không? Heck vâng. Chúng tôi đã thử tất cả các loại phương pháp nhân bản và điều này hoạt động tốt nhất. Tôi chắc rằng một số ninja có thể gợi ra một phương pháp nhanh hơn. Nhưng tôi nghi ngờ chúng ta đang nói về lợi nhuận cận biên.

Cách tiếp cận này chỉ đơn giản và dễ thực hiện. Gói nó vào một chức năng tiện lợi và nếu bạn thực sự cần phải vắt kiệt một số lợi ích, hãy đi sau một thời gian.

Bây giờ, đối với các đối tượng JavaScript không đơn giản, không có câu trả lời thực sự đơn giản. Thực tế, không thể vì tính chất động của các hàm JavaScript và trạng thái đối tượng bên trong. Nhân bản sâu một cấu trúc JSON với các hàm bên trong yêu cầu bạn tạo lại các hàm đó và bối cảnh bên trong của chúng. Và JavaScript đơn giản là không có cách chuẩn hóa để làm điều đó.

Cách chính xác để làm điều này, một lần nữa, là thông qua một phương thức tiện lợi mà bạn khai báo và sử dụng lại trong mã của mình. Phương pháp tiện lợi có thể được ban cho một số hiểu biết về các đối tượng của riêng bạn để bạn có thể đảm bảo tái tạo đúng biểu đồ trong đối tượng mới.

Chúng tôi tự viết, nhưng cách tiếp cận chung tốt nhất tôi từng thấy được đề cập ở đây:

http://davidwalsh.name/javascript-clone

Đây là ý tưởng đúng. Tác giả (David Walsh) đã bình luận về việc nhân bản các chức năng tổng quát. Đây là điều bạn có thể chọn để làm, tùy thuộc vào trường hợp sử dụng của bạn.

Ý tưởng chính là bạn cần xử lý đặc biệt việc khởi tạo các chức năng của bạn (hoặc các lớp nguyên mẫu, có thể nói) trên cơ sở mỗi loại. Tại đây, anh ấy đã cung cấp một vài ví dụ cho RegExp và Date.

Mã này không chỉ ngắn gọn mà còn rất dễ đọc. Nó khá dễ dàng để mở rộng.

Đây có phải là hiệu quả? Heck vâng. Cho rằng mục tiêu là tạo ra một bản sao sâu thực sự, sau đó bạn sẽ phải đi bộ các thành viên của biểu đồ đối tượng nguồn. Với phương pháp này, bạn có thể Tweak chính xác thành viên con nào sẽ điều trị và cách xử lý thủ công các loại tùy chỉnh.

Vì vậy, có bạn đi. Hai cách tiếp cận. Cả hai đều hiệu quả trong quan điểm của tôi.

15
Michael Uzquiano

Đây thường không phải là giải pháp hiệu quả nhất, nhưng nó làm những gì tôi cần. Các trường hợp thử nghiệm đơn giản dưới đây ...

function clone(obj, clones) {
    // Makes a deep copy of 'obj'. Handles cyclic structures by
    // tracking cloned obj's in the 'clones' parameter. Functions 
    // are included, but not cloned. Functions members are cloned.
    var new_obj,
        already_cloned,
        t = typeof obj,
        i = 0,
        l,
        pair; 

    clones = clones || [];

    if (obj === null) {
        return obj;
    }

    if (t === "object" || t === "function") {

        // check to see if we've already cloned obj
        for (i = 0, l = clones.length; i < l; i++) {
            pair = clones[i];
            if (pair[0] === obj) {
                already_cloned = pair[1];
                break;
            }
        }

        if (already_cloned) {
            return already_cloned; 
        } else {
            if (t === "object") { // create new object
                new_obj = new obj.constructor();
            } else { // Just use functions as is
                new_obj = obj;
            }

            clones.Push([obj, new_obj]); // keep track of objects we've cloned

            for (key in obj) { // clone object members
                if (obj.hasOwnProperty(key)) {
                    new_obj[key] = clone(obj[key], clones);
                }
            }
        }
    }
    return new_obj || obj;
}

Kiểm tra mảng tuần hoàn ...

a = []
a.Push("b", "c", a)
aa = clone(a)
aa === a //=> false
aa[2] === a //=> false
aa[2] === a[2] //=> false
aa[2] === aa //=> true

Kiểm tra chức năng ...

f = new Function
f.a = a
ff = clone(f)
ff === f //=> true
ff.a === a //=> false
13
neatonk

Tôi không đồng ý với câu trả lời có số phiếu lớn nhất tại đây . A Bản sao sâu đệ quy nhanh hơn nhiều so với Cách tiếp cận JSON.parse (JSON.opesify (obj)) đã đề cập.

Và đây là chức năng để tham khảo nhanh:

function cloneDeep (o) {
  let newO
  let i

  if (typeof o !== 'object') return o

  if (!o) return o

  if (Object.prototype.toString.apply(o) === '[object Array]') {
    newO = []
    for (i = 0; i < o.length; i += 1) {
      newO[i] = cloneDeep(o[i])
    }
    return newO
  }

  newO = {}
  for (i in o) {
    if (o.hasOwnProperty(i)) {
      newO[i] = cloneDeep(o[i])
    }
  }
  return newO
}
11
prograhammer

AngularJS

Vâng, nếu bạn đang sử dụng góc, bạn cũng có thể làm điều này

var newObject = angular.copy(oldObject);
11
azerafati
// obj target object, vals source object
var setVals = function (obj, vals) {
    if (obj && vals) {
        for (var x in vals) {
            if (vals.hasOwnProperty(x)) {
                if (obj[x] && typeof vals[x] === 'object') {
                    obj[x] = setVals(obj[x], vals[x]);
                } else {
                    obj[x] = vals[x];
                }
            }
        }
    }
    return obj;
};
10
Dima

Đối với những người muốn sử dụng phiên bản JSON.parse(JSON.stringify(obj)), nhưng không mất các đối tượng Ngày, bạn có thể sử dụng đối số giây của phương thức parse để chuyển đổi chuỗi trở lại Ngày:

function clone(obj) {
  var regExp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
  return JSON.parse(JSON.stringify(x), function(k, v) {
    if (typeof v === 'string' && regExp.test(v))
      return new Date(v);
    return v;
  });
}
8
Buzinas

Đây là một phương thức clone () toàn diện có thể sao chép bất kỳ đối tượng JavaScript nào. Nó xử lý hầu hết các trường hợp:

function clone(src, deep) {

    var toString = Object.prototype.toString;
    if (!src && typeof src != "object") {
        // Any non-object (Boolean, String, Number), null, undefined, NaN
        return src;
    }

    // Honor native/custom clone methods
    if (src.clone && toString.call(src.clone) == "[object Function]") {
        return src.clone(deep);
    }

    // DOM elements
    if (src.nodeType && toString.call(src.cloneNode) == "[object Function]") {
        return src.cloneNode(deep);
    }

    // Date
    if (toString.call(src) == "[object Date]") {
        return new Date(src.getTime());
    }

    // RegExp
    if (toString.call(src) == "[object RegExp]") {
        return new RegExp(src);
    }

    // Function
    if (toString.call(src) == "[object Function]") {

        //Wrap in another method to make sure == is not true;
        //Note: Huge performance issue due to closures, comment this :)
        return (function(){
            src.apply(this, arguments);
        });
    }

    var ret, index;
    //Array
    if (toString.call(src) == "[object Array]") {
        //[].slice(0) would soft clone
        ret = src.slice();
        if (deep) {
            index = ret.length;
            while (index--) {
                ret[index] = clone(ret[index], true);
            }
        }
    }
    //Object
    else {
        ret = src.constructor ? new src.constructor() : {};
        for (var prop in src) {
            ret[prop] = deep
                ? clone(src[prop], true)
                : src[prop];
        }
    }
    return ret;
};
8
user1547016

Tôi thường sử dụng var newObj = JSON.parse( JSON.stringify(oldObje) ); nhưng, đây là một cách thích hợp hơn:

var o = {};

var oo = Object.create(o);

(o === oo); // => false

Xem các trình duyệt cũ!

6
Cody

Tôi sử dụng thư viện nhân bản npm. Rõ ràng nó cũng hoạt động trong trình duyệt.

https://www.npmjs.com/package/clone

let a = clone(b)
6
user3071643

Chỉ khi bạn có thể sử dụng ECMAScript 6 hoặc bộ chuyển đổi .

Tính năng, đặc điểm:

  • Không kích hoạt getter/setter trong khi sao chép.
  • Bảo quản getter/setter.
  • Bảo quản thông tin nguyên mẫu.
  • Hoạt động với cả hai kiểu object-chữfunctionOO .

Mã số:

function clone(target, source){

    for(let key in source){

        // Use getOwnPropertyDescriptor instead of source[key] to prevent from trigering setter/getter.
        let descriptor = Object.getOwnPropertyDescriptor(source, key);
        if(descriptor.value instanceof String){
            target[key] = new String(descriptor.value);
        }
        else if(descriptor.value instanceof Array){
            target[key] = clone([], descriptor.value);
        }
        else if(descriptor.value instanceof Object){
            let prototype = Reflect.getPrototypeOf(descriptor.value);
            let cloneObject = clone({}, descriptor.value);
            Reflect.setPrototypeOf(cloneObject, prototype);
            target[key] = cloneObject;
        }
        else {
            Object.defineProperty(target, key, descriptor);
        }
    }
    let prototype = Reflect.getPrototypeOf(source);
    Reflect.setPrototypeOf(target, prototype);
    return target;
}
6
andrew

Tôi đến trễ để trả lời câu hỏi này, nhưng tôi có một cách khác để nhân bản đối tượng:

   function cloneObject(obj) {
        if (obj === null || typeof(obj) !== 'object')
            return obj;
        var temp = obj.constructor(); // changed
        for (var key in obj) {
            if (Object.prototype.hasOwnProperty.call(obj, key)) {
                obj['isActiveClone'] = null;
                temp[key] = cloneObject(obj[key]);
                delete obj['isActiveClone'];
            }
        }
        return temp;
    }



var b = cloneObject({"a":1,"b":2});   // calling

cái nào tốt hơn và nhanh hơn sau đó:

var a = {"a":1,"b":2};
var b = JSON.parse(JSON.stringify(a));  

var a = {"a":1,"b":2};

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

Tôi đã đánh dấu mã chuẩn và bạn có thể kiểm tra kết quả tại đây :

và chia sẻ kết quả: enter image description here Tài liệu tham khảo: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty

6
Mayur Agarwal

Nhân bản một đối tượng bằng JavaScript ngày nay: ECMAScript 2015 (trước đây gọi là ECMAScript 6)

var original = {a: 1};

// Method 1: New object with original assigned.
var copy1 = Object.assign({}, original);

// Method 2: New object with spread operator assignment.
var copy2 = {...original};

Các trình duyệt cũ có thể không hỗ trợ ECMAScript 2015. Một giải pháp phổ biến là sử dụng trình biên dịch JavaScript sang JavaScript như Babel để xuất ECMAScript 5 phiên bản mã JavaScript của bạn.

Như được chỉ ra bởi @ jim-hall , đây chỉ là một bản sao nông. Các thuộc tính của các thuộc tính được sao chép làm tham chiếu: thay đổi một thuộc tính sẽ thay đổi giá trị trong đối tượng/thể hiện khác.

6
Barry Staes

Giải pháp ECMAScript 6 một dòng (các loại đối tượng đặc biệt như Ngày/Regex không được xử lý):

const clone = (o) =>
  typeof o === 'object' && o !== null ?      // only clone objects
  (Array.isArray(o) ?                        // if cloning an array
    o.map(e => clone(e)) :                   // clone each of its elements
    Object.keys(o).reduce(                   // otherwise reduce every key in the object
      (r, k) => (r[k] = clone(o[k]), r), {}  // and save its cloned value into a new object
    )
  ) :
  o;                                         // return non-objects as is

var x = {
  nested: {
    name: 'test'
  }
};

var y = clone(x);

console.log(x.nested !== y.nested);

5
Shishir Arora

Lodash có một chức năng xử lý mà bạn thích như vậy.

var foo = {a: 'a', b: {c:'d', e: {f: 'g'}}};

var bar = _.cloneDeep(foo);
// bar = {a: 'a', b: {c:'d', e: {f: 'g'}}} 

Đọc tài liệu ở đây .

5
Daniel Barde

Đây là phương pháp nhanh nhất tôi đã tạo mà không sử dụng nguyên mẫu, vì vậy nó sẽ duy trì hasOwnProperty trong đối tượng mới.

Giải pháp là lặp lại các thuộc tính cấp cao nhất của đối tượng ban đầu, tạo hai bản sao, xóa từng thuộc tính khỏi bản gốc và sau đó đặt lại đối tượng ban đầu và trả lại bản sao mới. Nó chỉ phải lặp đi lặp lại nhiều lần như các thuộc tính cấp cao nhất. Điều này lưu tất cả các điều kiện if để kiểm tra xem mỗi thuộc tính là một hàm, đối tượng, chuỗi, v.v. và không phải lặp lại từng thuộc tính con cháu.

Hạn chế duy nhất là đối tượng ban đầu phải được cung cấp với không gian tên được tạo ban đầu, để thiết lập lại nó.

copyDeleteAndReset:function(namespace,strObjName){
    var obj = namespace[strObjName],
    objNew = {},objOrig = {};
    for(i in obj){
        if(obj.hasOwnProperty(i)){
            objNew[i] = objOrig[i] = obj[i];
            delete obj[i];
        }
    }
    namespace[strObjName] = objOrig;
    return objNew;
}

var namespace = {};
namespace.objOrig = {
    '0':{
        innerObj:{a:0,b:1,c:2}
    }
}

var objNew = copyDeleteAndReset(namespace,'objOrig');
objNew['0'] = 'NEW VALUE';

console.log(objNew['0']) === 'NEW VALUE';
console.log(namespace.objOrig['0']) === innerObj:{a:0,b:1,c:2};
4
Steve Tomlin

Để tham khảo trong tương lai, bản nháp hiện tại của ECMAScript 6 giới thiệu Object.assign như một cách nhân bản các đối tượng. Mã ví dụ sẽ là:

var obj1 = { a: true, b: 1 };
var obj2 = Object.assign(obj1);
console.log(obj2); // { a: true, b: 1 }

Tại thời điểm viết hỗ trợ chỉ giới hạn ở Firefox 34 trong các trình duyệt vì vậy, nó không thể sử dụng được trong mã sản xuất (trừ khi bạn đã viết một phần mở rộng của Firefox).

4
Robin Whittleton

Ví dụ ES 2017:

let objectToCopy = someObj;
let copyOfObject = {};
Object.defineProperties(copyOfObject, Object.getOwnPropertyDescriptors(objectToCopy));
// copyOfObject will now be the same as objectToCopy
4
codeMonkey

Điều gì về nhân bản đối tượng không đồng bộ được thực hiện bởi một Promise?

async function clone(thingy /**/)
{
    if(thingy instanceof Promise)
    {
        throw Error("This function cannot clone Promises.");
    }
    return thingy;
}

Trong JavaScript, bạn có thể viết phương thức deepCopy của mình như 

function deepCopy(src) {
  let target = Array.isArray(src) ? [] : {};
  for (let prop in src) {
    let value = src[prop];
    if(value && typeof value === 'object') {
      target[prop] = deepCopy(value);
  } else {
      target[prop] = value;
  }
 }
    return target;
}
3
chandan gupta

Có rất nhiều cách để đạt được điều này, nhưng nếu bạn muốn làm điều này mà không cần bất kỳ thư viện nào, bạn có thể sử dụng như sau:

const cloneObject = (oldObject) => {
  let newObject = oldObject;
  if (oldObject && typeof oldObject === 'object') {
    if(Array.isArray(oldObject)) {
      newObject = [];
    } else if (Object.prototype.toString.call(oldObject) === '[object Date]' && !isNaN(oldObject)) {
      newObject = new Date(oldObject.getTime());
    } else {
      newObject = {};
      for (let i in oldObject) {
        newObject[i] = cloneObject(oldObject[i]);
      }
    }

  }
  return newObject;
}

Cho tôi biết bạn nghĩ gì.

3
shobhit1

Đây là phiên bản của tôi về cloner đối tượng. Đây là phiên bản độc lập của phương thức jQuery, chỉ có một vài điều chỉnh và điều chỉnh. Kiểm tra fiddle . Tôi đã sử dụng rất nhiều jQuery cho đến ngày tôi nhận ra rằng tôi chỉ sử dụng chức năng này trong hầu hết thời gian x_x.

Cách sử dụng giống như được mô tả trong API jQuery:

  • Bản sao không sâu: extend(object_dest, object_source);
  • Bản sao sâu: extend(true, object_dest, object_source);

Một chức năng bổ sung được sử dụng để xác định nếu đối tượng là thích hợp để được nhân bản.

/**
 * This is a quasi clone of jQuery's extend() function.
 * by Romain WEEGER for wJs library - www.wexample.com
 * @returns {*|{}}
 */
function extend() {
    // Make a copy of arguments to avoid JavaScript inspector hints.
    var to_add, name, copy_is_array, clone,

    // The target object who receive parameters
    // form other objects.
    target = arguments[0] || {},

    // Index of first argument to mix to target.
    i = 1,

    // Mix target with all function arguments.
    length = arguments.length,

    // Define if we merge object recursively.
    deep = false;

    // Handle a deep copy situation.
    if (typeof target === 'boolean') {
        deep = target;

        // Skip the boolean and the target.
        target = arguments[ i ] || {};

        // Use next object as first added.
        i++;
    }

    // Handle case when target is a string or something (possible in deep copy)
    if (typeof target !== 'object' && typeof target !== 'function') {
        target = {};
    }

    // Loop trough arguments.
    for (false; i < length; i += 1) {

        // Only deal with non-null/undefined values
        if ((to_add = arguments[ i ]) !== null) {

            // Extend the base object.
            for (name in to_add) {

                // We do not wrap for loop into hasOwnProperty,
                // to access to all values of object.
                // Prevent never-ending loop.
                if (target === to_add[name]) {
                    continue;
                }

                // Recurse if we're merging plain objects or arrays.
                if (deep && to_add[name] && (is_plain_object(to_add[name]) || (copy_is_array = Array.isArray(to_add[name])))) {
                    if (copy_is_array) {
                        copy_is_array = false;
                        clone = target[name] && Array.isArray(target[name]) ? target[name] : [];
                    }
                    else {
                        clone = target[name] && is_plain_object(target[name]) ? target[name] : {};
                    }

                    // Never move original objects, clone them.
                    target[name] = extend(deep, clone, to_add[name]);
                }

                // Don't bring in undefined values.
                else if (to_add[name] !== undefined) {
                    target[name] = to_add[name];
                }
            }
        }
    }
    return target;
}

/**
 * Check to see if an object is a plain object
 * (created using "{}" or "new Object").
 * Forked from jQuery.
 * @param obj
 * @returns {boolean}
 */
function is_plain_object(obj) {
    // Not plain objects:
    // - Any object or value whose internal [[Class]] property is not "[object Object]"
    // - DOM nodes
    // - window
    if (obj === null || typeof obj !== "object" || obj.nodeType || (obj !== null && obj === obj.window)) {
        return false;
    }
    // Support: Firefox <20
    // The try/catch suppresses exceptions thrown when attempting to access
    // the "constructor" property of certain Host objects, i.e. |window.location|
    // https://bugzilla.mozilla.org/show_bug.cgi?id=814622
    try {
        if (obj.constructor && !this.hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf")) {
            return false;
        }
    }
    catch (e) {
        return false;
    }

    // If the function hasn't returned already, we're confident that
    // |obj| is a plain object, created by {} or constructed with new Object
    return true;
}
3
weeger

Tôi nghĩ rằng đây là giải pháp tốt nhất nếu bạn muốn khái quát thuật toán nhân bản đối tượng của mình.
..

function clone(obj){
    if(typeof(obj) == 'function')//it's a simple function
        return obj;
    //of it's not an object (but could be an array...even if in javascript arrays are objects)
    if(typeof(obj) !=  'object' || obj.constructor.toString().indexOf('Array')!=-1)
        if(JSON != undefined)//if we have the JSON obj
            try{
                return JSON.parse(JSON.stringify(obj));
            }catch(err){
                return JSON.parse('"'+JSON.stringify(obj)+'"');
            }
        else
            try{
                return eval(uneval(obj));
            }catch(err){
                return eval('"'+uneval(obj)+'"');
            }
    // I used to rely on jQuery for this, but the "extend" function returns
    //an object similar to the one cloned,
    //but that was not an instance (instanceof) of the cloned class
    /*
    if(jQuery != undefined)//if we use the jQuery plugin
        return jQuery.extend(true,{},obj);
    else//we recursivley clone the object
    */
    return (function _clone(obj){
        if(obj == null || typeof(obj) != 'object')
            return obj;
        function temp () {};
        temp.prototype = obj;
        var F = new temp;
        for(var key in obj)
            F[key] = clone(obj[key]);
        return F;
    })(obj);            
}
3
gion_13

Có rất nhiều câu trả lời, nhưng không ai trong số họ đưa ra hiệu quả mong muốn mà tôi cần. Tôi muốn sử dụng sức mạnh của bản sao sâu của jQuery ... Tuy nhiên, khi nó chạy vào một mảng, nó chỉ cần sao chép tham chiếu đến mảng và sao chép sâu các mục trong đó. Để giải quyết vấn đề này, tôi đã tạo một hàm đệ quy nhỏ đẹp sẽ tự động tạo một mảng mới. 

Vì vậy, để sao chép hoàn toàn một mục hiện có, bạn chỉ cần làm điều này:

var newItem = jQuery.extend(true, {}, oldItem);
createNewArrays(newItem);


function createNewArrays(obj) {
    for (var prop in obj) {
        if ((kendo != null && obj[prop] instanceof kendo.data.ObservableArray) || obj[prop] instanceof Array) {
            var copy = [];
            $.each(obj[prop], function (i, item) {
                var newChild = $.extend(true, {}, item);
                createNewArrays(newChild);
                copy.Push(newChild);
            });
            obj[prop] = copy;
        }
    }
}
3
Daniel Lorenz

Đây là cách tôi nhân bản sâu một đối tượng với ES2015 giá trị mặc định và toán tử trải rộng

 const makeDeepCopy = (obj, copy = {}) => {
  for (let item in obj) {
    if (typeof obj[item] === 'object') {
      makeDeepCopy(obj[item], copy)
    }
    if (obj.hasOwnProperty(item)) {
      copy = {
        ...obj
      }
    }
  }
  return copy
}

const testObj = {
  "type": "object",
  "properties": {
    "userId": {
      "type": "string",
      "chance": "guid"
    },
    "emailAddr": {
      "type": "string",
      "chance": {
        "email": {
          "domain": "fake.com"
        }
      },
      "pattern": "[email protected]"
    }
  },
  "required": [
    "userId",
    "emailAddr"
  ]
}

const makeDeepCopy = (obj, copy = {}) => {
  for (let item in obj) {
    if (typeof obj[item] === 'object') {
      makeDeepCopy(obj[item], copy)
    }
    if (obj.hasOwnProperty(item)) {
      copy = {
        ...obj
      }
    }
  }
  return copy
}

console.log(makeDeepCopy(testObj))

3
Julez

Nếu không chạm vào sự kế thừa nguyên mẫu, bạn có thể khắc sâu các đối tượng và mảng như sau;

function objectClone(o){
  var ot = Array.isArray(o);
  return o !== null && typeof o === "object" ? Object.keys(o)
                                                     .reduce((r,k) => o[k] !== null && typeof o[k] === "object" ? (r[k] = objectClone(o[k]),r)
                                                                                                                : (r[k] = o[k],r), ot ? [] : {})
                                             : o;
}
var obj = {a: 1, b: {c: 2, d: {e: 3, f: {g: 4, h: null}}}},
    arr = [1,2,[3,4,[5,6,[7]]]],
    nil = null,
  clobj = objectClone(obj),
  clarr = objectClone(arr),
  clnil = objectClone(nil);
console.log(clobj, obj === clobj);
console.log(clarr, arr === clarr);
console.log(clnil, nil === clnil);
clarr[2][2][2] = "seven";
console.log(arr, clarr);

2
Redu

Đối với một bản sao nông, có một phương pháp đơn giản, tuyệt vời được giới thiệu trong tiêu chuẩn ECMAScript2018. Nó liên quan đến việc sử dụng Toán tử trải rộng :

let obj = {a : "foo", b:"bar" , c:10 , d:true , e:[1,2,3] };

let objClone = { ...obj };

Tôi đã thử nghiệm nó trong trình duyệt Chrome, cả hai đối tượng được lưu trữ ở các vị trí khác nhau, vì vậy việc thay đổi giá trị con ngay lập tức sẽ không thay đổi đối tượng khác. Mặc dù (trong ví dụ) thay đổi giá trị trong e sẽ ảnh hưởng đến cả hai bản sao.

Kỹ thuật này rất đơn giản và thẳng tiến. Tôi coi đây là một thực tiễn tốt nhất thực sự cho câu hỏi này một lần và mãi mãi. 

2
Vikram K

Sử dụng Object.create() để nhận prototype và hỗ trợ cho instanceof và sử dụng vòng lặp for() để nhận được vô số khóa:

function cloneObject(source) {
    var key,value;
    var clone = Object.create(source);

    for (key in source) {
        if (source.hasOwnProperty(key) === true) {
            value = source[key];

            if (value!==null && typeof value==="object") {
                clone[key] = cloneObject(value);
            } else {
                clone[key] = value;
            }
        }
    }

    return clone;
}
2
Steven Vachon

class Handler {
  static deepCopy (obj) {
    if (Object.prototype.toString.call(obj) === '[object Array]') {
      const result = [];
      
      for (let i = 0, len = obj.length; i < len; i++) {
        result[i] = Handler.deepCopy(obj[i]);
      }
      return result;
    } else if (Object.prototype.toString.call(obj) === '[object Object]') {
      const result = {};
      for (let prop in obj) {
        result[prop] = Handler.deepCopy(obj[prop]);
      }
      return result;
    }
    return obj;
  }
}

2
Ihor Pavlyk

Yêu cầu trình duyệt mới, nhưng ...

Hãy mở rộng Đối tượng bản địa và nhận real.extend();

Object.defineProperty(Object.prototype, 'extend', {
    enumerable: false,
    value: function(){
        var that = this;

        Array.prototype.slice.call(arguments).map(function(source){
            var props = Object.getOwnPropertyNames(source),
                i = 0, l = props.length,
                prop;

            for(; i < l; ++i){
                prop = props[i];

                if(that.hasOwnProperty(prop) && typeof(that[prop]) === 'object'){
                    that[prop] = that[prop].extend(source[prop]);
                }else{
                    Object.defineProperty(that, prop, Object.getOwnPropertyDescriptor(source, prop));
                }
            }
        });

        return this;
    }
});

Chỉ cần bật trước bất kỳ mã nào sử dụng .extend () trên một đối tượng.

Thí dụ:

var obj1 = {
    node1: '1',
    node2: '2',
    node3: 3
};

var obj2 = {
    node1: '4',
    node2: 5,
    node3: '6'
};

var obj3 = ({}).extend(obj1, obj2);

console.log(obj3);
// Object {node1: "4", node2: 5, node3: "6"}
2
Tristian

Năm 2019 tôi đang sử dụng:

deepCopy(object) {
  const getCircularReplacer = () => {
    const seen = new WeakSet();
    return (key, value) => {
      if(typeof value === 'object' && value !== null) {
        if(seen.has(value)) return;
        seen.add(value);
      }
      return value;
    };
  };
  return JSON.parse(JSON.stringify(object, getCircularReplacer()));
}

const theCopy = deepCopy(originalObject);

2
Matthieu Chavigny

Vì đệ quy quá đắt đối với JavaScript và hầu hết các câu trả lời tôi đã tìm thấy đều sử dụng đệ quy, trong khi cách tiếp cận JSON sẽ bỏ qua các phần không chuyển đổi JSON (Hàm, v.v.). Vì vậy, tôi đã làm một nghiên cứu nhỏ và tìm thấy kỹ thuật trampoline này để tránh nó. Đây là mã:

/*
 * Trampoline to avoid recursion in JavaScript, see:
 *     http://www.integralist.co.uk/posts/js-recursion.html
 */
function trampoline() {
    var func = arguments[0];
    var args = [];
    for (var i = 1; i < arguments.length; i++) {
        args[i - 1] = arguments[i];
    }

    var currentBatch = func.apply(this, args);
    var nextBatch = [];

    while (currentBatch && currentBatch.length > 0) {
        currentBatch.forEach(function(eachFunc) {
            var ret = eachFunc();
            if (ret && ret.length > 0) {
                nextBatch = nextBatch.concat(ret);
            }
        });

        currentBatch = nextBatch;
        nextBatch = [];
    }
};

/*
 *  Deep clone an object using the trampoline technique.
 *
 *  @param target {Object} Object to clone
 *  @return {Object} Cloned object.
 */
function clone(target) {
    if (typeof target !== 'object') {
        return target;
    }
    if (target == null || Object.keys(target).length == 0) {
        return target;
    }

    function _clone(b, a) {
        var nextBatch = [];
        for (var key in b) {
            if (typeof b[key] === 'object' && b[key] !== null) {
                if (b[key] instanceof Array) {
                    a[key] = [];
                }
                else {
                    a[key] = {};
                }
                nextBatch.Push(_clone.bind(null, b[key], a[key]));
            }
            else {
                a[key] = b[key];
            }
        }
        return nextBatch;
    };

    var ret = target instanceof Array ? [] : {};
    (trampoline.bind(null, _clone))(target, ret);
    return ret;
};

Cũng xem Gist này: https://Gist.github.com/SeanOceanHu/7594cafbfab682f790eb

2
Bodhi Hu

Để tham khảo trong tương lai, người ta có thể sử dụng mã này

ES6:

_clone: function(obj){
    let newObj = {};
    for(let i in obj){
        if(typeof(obj[i]) === 'object' && Object.keys(obj[i]).length){
            newObj[i] = clone(obj[i]);
        } else{
            newObj[i] = obj[i];
        }
    }
    return Object.assign({},newObj);
}

ES5:

function clone(obj){
let newObj = {};
for(let i in obj){
    if(typeof(obj[i]) === 'object' && Object.keys(obj[i]).length){
        newObj[i] = clone(obj[i]);
    } else{
        newObj[i] = obj[i];
    }
}
return Object.assign({},newObj);

}

Ví dụ 

var obj ={a:{b:1,c:3},d:4,e:{f:6}}
var xc = clone(obj);
console.log(obj); //{a:{b:1,c:3},d:4,e:{f:6}}
console.log(xc); //{a:{b:1,c:3},d:4,e:{f:6}}

xc.a.b = 90;
console.log(obj); //{a:{b:1,c:3},d:4,e:{f:6}}
console.log(xc); //{a:{b:90,c:3},d:4,e:{f:6}}
2
Ashutosh Jha

Object.assign({},sourceObj) chỉ nhân bản đối tượng nếu thuộc tính của chúng không có khóa loại tham chiếu. cũ

obj={a:"lol",b:["yes","no","maybe"]}
clonedObj = Object.assign({},obj);

clonedObj.b.Push("skip")// changes will reflected to the actual obj as well because of its reference type.
obj.b //will also console => yes,no,maybe,skip

Vì vậy, đối với nhân bản sâu là không thể đạt được theo cách này.

Giải pháp tốt nhất hiệu quả là

var obj = Json.stringify(yourSourceObj)
var cloned = Json.parse(obj);
2
KRIPA SHANKAR JHA

Theo kinh nghiệm của tôi, một phiên bản đệ quy vượt trội hơn nhiều so với JSON.parse(JSON.stringify(obj)). Dưới đây là một chức năng sao chép đối tượng sâu đệ quy được hiện đại hóa có thể phù hợp trên một dòng duy nhất:

function deepCopy(obj) {
  return Object.keys(obj).reduce((v, d) => Object.assign(v, {
    [d]: (obj[d].constructor === Object) ? deepCopy(obj[d]) : obj[d]
  }), {});
}

Điều này đang thực hiện nhanh hơn 40 lần so với phương thức JSON.parse....

2
Parabolord

Có ba cách khác nhau để sao chép các đối tượng trong Javascript.

1: Sao chép sâu bằng cách sử dụng phép lặp

function iterationCopy(src) {
  let target = {};
  for (let prop in src) {
    if (src.hasOwnProperty(prop)) {
      target[prop] = src[prop];
    }
  }
  return target;
}
const source = {a:1, b:2, c:3};
const target = iterationCopy(source);
console.log(target); // {a:1, b:2, c:3}
// Check if clones it and not changing it
source.a = 'a';
console.log(source.a); // 'a'
console.log(target.a); // 1

Vì vậy, như bạn thấy, nó làm việc!

Bây giờ, hãy để Lát cắt theo đuổi giải pháp thứ hai thực sự thanh lịch hơn, nhưng hạn chế sử dụng hơn.

2: Chuyển đổi sang JSON và quay lại

function jsonCopy(src) {
  return JSON.parse(JSON.stringify(src));
}
const source = {a:1, b:2, c:3};
const target = jsonCopy(source);
console.log(target); // {a:1, b:2, c:3}
// Check if clones it and not changing it
source.a = 'a';
console.log(source.a); // 'a'
console.log(target.a); // 1

Lưu ý: Hãy cẩn thận khi sử dụng phương thức này vì đối tượng nguồn của bạn PHẢI an toàn JSON. Vì vậy, nó có thể cần một số loại xử lý ngoại lệ để giữ an toàn trong trường hợp đối tượng nguồn không thể chuyển đổi thành JSON.

3: Sử dụng Object.assign

Cập nhật: Phương pháp này có một lỗ hổng là nó chỉ thực hiện một bản sao nông. Nó có nghĩa là các thuộc tính lồng nhau vẫn sẽ được sao chép bằng cách tham chiếu. Hãy cẩn thận về nó.

Cách này là cách tốt nhất và an toàn nhất mà cá nhân tôi tiêu thụ trong các dự án của mình. Nó sử dụng một phương thức tĩnh tích hợp trên đối tượng Object và được xử lý và cung cấp bởi ngôn ngữ. Vì vậy, sử dụng cái này!

function bestCopyEver(src) {
  return Object.assign({}, src);
}
const source = {a:1, b:2, c:3};
const target = bestCopyEver(source);
console.log(target); // {a:1, b:2, c:3}
// Check if clones it and not changing it
source.a = 'a';
console.log(source.a); // 'a'
console.log(target.a); // 1
2
Kishan Patel

Đây là một giải pháp với đệ quy:

obj = {
  a: { b: { c: { d: ['1', '2'] } } },
  e: 'Saeid'
}
const Clone = function (obj) {
  
  const container = Array.isArray(obj) ? [] : {}
  const keys  = Object.keys(obj)
   
  for (let i = 0; i < keys.length; i++) {
    const key = keys[i]
    if(typeof obj[key] == 'object') {
      container[key] = Clone(obj[key])
    }
    else
      container[key] = obj[key].slice()
  }
  
  return container
}
 console.log(Clone(obj))

2
SAlidadi

nếu bạn thấy mình thường xuyên thực hiện loại điều này (ví dụ: tạo chức năng hoàn tác lại) thì có thể đáng để xem xét Immutable.js

const map1 = Immutable.fromJS( { a: 1, b: 2, c: { d: 3 } } );
const map2 = map1.setIn( [ 'c', 'd' ], 50 );

console.log( `${ map1.getIn( [ 'c', 'd' ] ) } vs ${ map2.getIn( [ 'c', 'd' ] ) }` ); // "3 vs 50"

https://codepen.io/anon/pen/OBpqNE?editors=1111

1
shunryu111

Nhìn qua danh sách dài các câu trả lời này gần như tất cả các giải pháp đã được đề cập ngoại trừ một giải pháp mà tôi biết. Đây là danh sách các cách Vanilla JS nhân bản sâu một đối tượng.

  1. JSON.parse (JSON.opesify (obj));

  2. Thông qua history.state với PushState hoặc thay thếState

  3. API thông báo web nhưng điều này có nhược điểm là yêu cầu người dùng cấp quyền.

  4. Thực hiện vòng lặp đệ quy của riêng bạn thông qua đối tượng để sao chép từng cấp độ.

  5. Câu trả lời tôi không thấy -> Sử dụng ServiceWorkers. Các thông điệp (đối tượng) được truyền qua lại giữa trang và tập lệnh ServiceWorker sẽ là bản sao sâu của bất kỳ đối tượng nào.

1
Steve Griffith

Vì câu hỏi này có rất nhiều sự chú ý và câu trả lời có liên quan đến các tính năng sẵn có như Object.assign hoặc mã tùy chỉnh để sao chép sâu, tôi muốn chia sẻ một số thư viện để sao chép sâu, 

1. esclone

cài đặt npm --sattedev esclone https://www.npmjs.com/package/esclone

Ví dụ sử dụng trong ES6:

import esclone from "esclone";

const rockysGrandFather = {
  name: "Rockys grand father",
  father: "Don't know :("
};
const rockysFather = {
  name: "Rockys Father",
  father: rockysGrandFather
};

const rocky = {
  name: "Rocky",
  father: rockysFather
};

const rockyClone = esclone(rocky);

Ví dụ sử dụng trong ES5:

var esclone = require("esclone")
var foo = new String("abcd")
var fooClone = esclone.default(foo)
console.log(fooClone)
console.log(foo === fooClone)

2. bản sao sâu

npm cài đặt deep-copy https://www.npmjs.com/package/deep-copy

Thí dụ:

var dcopy = require('deep-copy')

// deep copy object 
var copy = dcopy({a: {b: [{c: 5}]}})

// deep copy array 
var copy = dcopy([1, 2, {a: {b: 5}}])

3. clone-sâu

$ npm cài đặt --save clone-deep https://www.npmjs.com/package/clone-deep

Thí dụ:

var cloneDeep = require('clone-deep');

var obj = {a: 'b'};
var arr = [obj];

var copy = cloneDeep(arr);
obj.c = 'd';

console.log(copy);
//=> [{a: 'b'}] 

console.log(arr);
1
JTeam

Kịch bản của tôi là một chút khác nhau. Tôi đã có một đối tượng với các đối tượng lồng nhau cũng như các chức năng. Do đó, Object.assign()JSON.stringify() không phải là giải pháp cho vấn đề của tôi. Sử dụng thư viện của bên thứ ba cũng không phải là một lựa chọn cho tôi.

Do đó, tôi quyết định tạo một hàm đơn giản để sử dụng các phương thức dựng sẵn để sao chép một đối tượng với các thuộc tính theo nghĩa đen, các đối tượng lồng nhau và các hàm của nó.

let deepCopy = (target, source) => {
    Object.assign(target, source);
    // check if there's any nested objects
    Object.keys(source).forEach((prop) => {
        /**
          * assign function copies functions and
          * literals (int, strings, etc...)
          * except for objects and arrays, so:
          */
        if (typeof(source[prop]) === 'object') {
            // check if the item is, in fact, an array
            if (Array.isArray(source[prop])) {
                // clear the copied referenece of nested array
                target[prop] = Array();
                // iterate array's item and copy over
                source[prop].forEach((item, index) => {
                    // array's items could be objects too!
                    if (typeof(item) === 'object') {
                        // clear the copied referenece of nested objects
                        target[prop][index] = Object();
                        // and re do the process for nested objects
                        deepCopy(target[prop][index], item);
                    } else {
                        target[prop].Push(item);
                    }
                });
            // otherwise, treat it as an object
            } else {
                // clear the copied referenece of nested objects
                target[prop] = Object();
                // and re do the process for nested objects
                deepCopy(target[prop], source[prop]);
            }
        }
    });
};

Đây là mã kiểm tra:

let a = {
    name: 'Human', 
    func: () => {
        console.log('Hi!');
    }, 
    prop: {
        age: 21, 
        info: {
            hasShirt: true, 
            hasHat: false
        }
    },
    mark: [89, 92, { exam: [1, 2, 3] }]
};

let b = Object();

deepCopy(b, a);

a.name = 'Alien';
a.func = () => { console.log('Wassup!'); };
a.prop.age = 1024;
a.prop.info.hasShirt = false;
a.mark[0] = 87;
a.mark[1] = 91;
a.mark[2].exam = [4, 5, 6];

console.log(a); // updated props
console.log(b);

Đối với các mối quan tâm liên quan đến hiệu quả, tôi tin rằng đây là giải pháp đơn giản và hiệu quả nhất cho vấn đề tôi gặp phải. Tôi sẽ đánh giá cao bất kỳ ý kiến ​​về thuật toán này có thể làm cho nó hiệu quả hơn.

1
Kamyar

Khi đối tượng của bạn được lồng và nó chứa đối tượng dữ liệu, đối tượng có cấu trúc khác hoặc một số đối tượng thuộc tính, v.v. sau đó sử dụng JSON.parse(JSON.stringify(object)) hoặc Object.assign({}, obj) hoặc $.extend(true, {}, obj) sẽ không hoạt động. Trong trường hợp đó sử dụng lodash. Nó rất đơn giản và dễ dàng ..

var obj = {a: 25, b: {a: 1, b: 2}, c: new Date(), d: anotherNestedObject };
var A = _.cloneDeep(obj);

Bây giờ A sẽ là bản sao mới của obj mà không có bất kỳ tài liệu tham khảo nào .. 

1
Prasanth Jaya

Hi vọng điêu nay co ich.

function deepClone(obj) {
    /*
     * Duplicates an object 
     */

    var ret = null;
    if (obj !== Object(obj)) { // primitive types
        return obj;
    }
    if (obj instanceof String || obj instanceof Number || obj instanceof Boolean) { // string objecs
        ret = obj; // for ex: obj = new String("Spidergap")
    } else if (obj instanceof Date) { // date
        ret = new obj.constructor();
    } else
        ret = Object.create(obj.constructor.prototype);

    var prop = null;
    var allProps = Object.getOwnPropertyNames(obj); //gets non enumerables also


    var props = {};
    for (var i in allProps) {
        prop = allProps[i];
        props[prop] = false;
    }

    for (i in obj) {
        props[i] = i;
    }

    //now props contain both enums and non enums 
    var propDescriptor = null;
    var newPropVal = null; // value of the property in new object
    for (i in props) {
        prop = obj[i];
        propDescriptor = Object.getOwnPropertyDescriptor(obj, i);

        if (Array.isArray(prop)) { //not backward compatible
            prop = prop.slice(); // to copy the array
        } else
        if (prop instanceof Date == true) {
            prop = new prop.constructor();
        } else
        if (prop instanceof Object == true) {
            if (prop instanceof Function == true) { // function
                if (!Function.prototype.clone) {
                    Function.prototype.clone = function() {
                        var that = this;
                        var temp = function tmp() {
                            return that.apply(this, arguments);
                        };
                        for (var ky in this) {
                            temp[ky] = this[ky];
                        }
                        return temp;
                    }
                }
                prop = prop.clone();

            } else // normal object 
            {
                prop = deepClone(prop);
            }

        }

        newPropVal = {
            value: prop
        };
        if (propDescriptor) {
            /*
             * If property descriptors are there, they must be copied
             */
            newPropVal.enumerable = propDescriptor.enumerable;
            newPropVal.writable = propDescriptor.writable;

        }
        if (!ret.hasOwnProperty(i)) // when String or other predefined objects
            Object.defineProperty(ret, i, newPropVal); // non enumerable

    }
    return ret;
}

https://github.com/jinujd/Javascript-Deep-Clone

1
Jinu Joseph Daniel

Nếu bạn đang sử dụng es6, bạn chỉ cần thực hiện điều này bằng cách sử dụng toán tử trải.

let a = {id:1, name:'sample_name'}
let b = {...a};
1
Amaldev ps

Bạn có thể sử dụng toán tử lây lan để sao chép đối tượng trong JavaScript.

// toán tử lây lan làm công việc concat

let arr = [1,2,3];  let arr2 = [4,5];  arr = [...arr,...arr2]; console.log(arr); 
0
Raghul Shree

Đây là giải pháp của tôi mà không cần sử dụng bất kỳ thư viện hoặc chức năng javascript gốc nào.

function deepClone(obj) {
  if (typeof obj !== "object") {
    return obj;
  } else {
    let newObj =
      typeof obj === "object" && obj.length !== undefined ? [] : {};
    for (let key in obj) {
      if (key) {
        newObj[key] = deepClone(obj[key]);
      }
    }
    return newObj;
  }
}
0
Ankur Kedia

Làm thế nào về việc hợp nhất chìa khóa của đối tượng với nó giá trị?

function deepClone(o) {
    var keys = Object.keys(o);
    var values = Object.values(o);

    var clone = {};

    keys.forEach(function(key, i) {
        clone[key] = typeof values[i] == 'object' ? Object.create(values[i]) : values[i];
    });

    return clone;
}

Lưu ý: Phương thức này không nhất thiết tạo các bản sao nông, nhưng nó chỉ sao chép với độ sâu của một đối tượng bên trong, nghĩa là khi bạn được cung cấp một cái gì đó như {a: {b: {c: null}}}, nó sẽ chỉ sao chép các đối tượng trực tiếp bên trong chúng, vì vậy deepClone(a.b).c về mặt kỹ thuật là một tham chiếu đến a.b.c, trong khi deepClone(a).b là một bản sao, không phải là tài liệu tham khảo.

0
Eternal Darkness

Với đề xuất của phương thức mới Object.fromEntries () được hỗ trợ trên các phiên bản mới hơn của một số trình duyệt ( tham chiếu ). Tôi muốn đóng góp với phương pháp đệ quy tiếp theo:

const obj = {
  key1: {key11: "key11", key12: "key12", key13: {key131: 22}},
  key2: {key21: "key21", key22: "key22"},
  key3: "key3",
  key4: [1,2,3, {key: "value"}]
}

const cloneObj = (obj) =>
{
    if (Object(obj) !== obj)
       return obj;
    else if (Array.isArray(obj))
       return obj.map(cloneObj);

    return Object.fromEntries(Object.entries(obj).map(
        ([k,v]) => ([k, cloneObj(v)])
    ));
}

// Clone the original object.
let newObj = cloneObj(obj);

// Make changes on the original object.
obj.key1.key11 = "TEST";
obj.key3 = "TEST";
obj.key1.key13.key131 = "TEST";
obj.key4[1] = "TEST";
obj.key4[3].key = "TEST";

// Display both objects on the console.
console.log("Original object: ", obj);
console.log("Cloned object: ", newObj);
.as-console {background-color:black !important; color:Lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
0
Shidersz
function clone(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] = clone(obj[i]);
        }
        return copy;
    }

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

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

sử dụng phương pháp sau thay vì JSON.parse(JSON.stringify(obj)) vì nó chậm hơn phương thức sau 

Làm cách nào để sao chép chính xác một đối tượng JavaScript?

0
shakthi nagaraj