什么是最有效的方式来克隆一个JavaScript对象?我已经看到obj = eval(uneval(o));被使用,但它是非标准的,仅支持Firefox.我做了事情,如obj = JSON.parse(JSON.stringify(o));但质疑效率。


当前回答

结构化克隆

HTML 标准包含一个内部结构化的克隆/序列化算法,可以创建对象的深度克隆,它仍然仅限于某些内置类型,但除了由 JSON 支持的少数类型之外,它还支持日期、RegExps、地图、套件、Blobs、FileLists、ImageDatas、Sparse Arrays、Typed Arrays等。

结构化Clone 全球功能由 Node 17.0 提供:

const clone = structuredClone(original);

以前版本: Node.js 的 v8 模块(如 Node 11 )直接展示了结构化序列化 API,但此功能仍然被标记为“实验性”,并在未来的版本中可更改或删除。

const v8 = require('v8');

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

直接支持浏览器:可用于Firefox 94

const clone = structuredClone(original);

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

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

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

雖然同步,但這可能非常慢。 它發生了所有與操縱瀏覽器歷史相關的頭部. 重複召喚這種方法可能會導致Chrome暫時變成無責任。

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

其他回答

只是因为我没有看到AngularJS提到并认为人们可能想知道......

angular.copy 还提供深复制对象和序列的方法。

对于未来的参考,目前的 ECMAScript 6 草案将 Object.assign 引入为克隆对象的一种方式。

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

在编写时,支持仅限于Firefox 34在浏览器中,所以它还不能在生产代码中使用(除非您正在编写Firefox扩展)。

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

这里是一个全面的克隆()方法,可以克隆任何JavaScript对象,它处理几乎所有情况:

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

有很多答案,但没有一个给了我需要的效果,我想利用jQuery的深复制的力量......但是,当它进入一个序列时,它只是复制了对序列的参考,并深复制了其中内容。

(它甚至检查 kendo.data.ObservableArray 如果你想要它! 但是, 确保你打电话 kendo.observable(newItem) 如果你想 Arrays 再次可观察。

所以,要完全复制一个现有项目,你只是这样做:

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