javascript - 配列 - typescript オブジェクト コピー




JavaScriptでオブジェクトを深くクローンする最も効率的な方法は何ですか? (20)

注:これは、この質問に対する適切な回答ではなく、別の回答に対する回答です。 オブジェクトのクローニングを高速にしたい場合は、この質問に対する回答でCorbanの助言に従ってください。

jQueryの .clone()メソッドはDOM要素のみをクローンします。 JavaScriptオブジェクトを複製するには、次のようにします。

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

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

詳細は、 jQueryのマニュアルを参照してください

深いコピーは実際には上に示したものよりもはるかにスマートであることに注意してください - それは(例えばDOM要素を深く拡張しようとする)多くのトラップを避けることができます。 これはjQueryコアとプラグインで頻繁に使用されています。

JavaScriptオブジェクトを複製する最も効率的な方法は何ですか? 私はobj = eval(uneval(o)); 使用されていますが、これは非標準でFirefoxのみでサポートされています。

私はobj = JSON.parse(JSON.stringify(o));ようなことをしましたobj = JSON.parse(JSON.stringify(o)); 効率について疑問を呈します。

私はまた、さまざまな欠陥を持つ再帰的なコピー機能を見てきました。
私は標準的な解決策が存在しないことに驚いている。


1つのコード行でオブジェクトを複製する(ディープクローンではない)効率的な方法

Object.assignメソッドは、ECMAScript 2015(ES6)標準の一部であり、必要なものを正確に実行します。

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

Object.assign()メソッドは、すべての列挙可能なプロパティの値を1つ以上のソースオブジェクトからターゲットオブジェクトにコピーするために使用されます。

Object.assign

古いブラウザをサポートするためのpolyfill

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

AngularJS

あなたが角度を使用している場合は、これもできる

var newObject = angular.copy(oldObject);

JavaScriptのオブジェクトを深くコピーする(私は最高で最も単純だと思う)

1. JSON.parse(JSON.stringify(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.作成されたメソッドの使用

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. Lo-Dashの_.cloneDeepリンク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. 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 }  

いつも間違っている

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. Underscore.jsを使用する_.clone link 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 }  

いつも間違っている

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

media.comを参照

JSBEN.CHパフォーマンスベンチマークプレイグラウンド1〜3 http://jsben.ch/KVQLd


Crockfordは、この関数を使うことを提案しています。

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

var newObject = object(oldObject);

それは簡潔で、期待どおりに機能し、ライブラリは必要ありません。

編集:

これはpolyfillのためObject.create、これも使用できます。

var newObject = Object.create(oldObject);

注:いくつかを使用する場合は、使用する一部の反復で問題が発生することがありますhasOwnProperty。なぜなら、create継承する新しい空のオブジェクトを作成するからoldObjectです。しかし、オブジェクトの複製にはまだ有用で実用的です。

例として、if oldObject.a = 5;

newObject.a; // is 5

しかし:

oldObject.hasOwnProperty(a); // is true
newObject.hasOwnProperty(a); // is false

このベンチマークをチェックアウト: http://jsben.ch/#/bWfk9 : http://jsben.ch/#/bWfk9

スピードが主な関心事だった以前のテストでは、

JSON.parse(JSON.stringify(obj))

オブジェクトを深くクローンする最速の方法です(深いフ​​ラグをtrueに設定してjQuery.extendを10〜20% jQuery.extend )。

ディープフラグがfalse(浅いクローン)に設定されていると、jQuery.extendはかなり高速です。 型バリデーションのための余分なロジックが含まれており、未定義のプロパティなどをコピーしないため、これは良い選択ですが、これによっても少し時間がかかります。

クローンを作成しようとしているオブジェクトの構造を知っていたり、深いネストされた配列を避けることができれば、hasOwnPropertyをチェックしながらオブジェクトをクローンするfor (var i in obj)の単純なfor (var i in obj)ループを書くことができ、jQueryよりもはるかに高速です。

最後に、ホットループで既知のオブジェクト構造を複製しようとすると、クローン手順を単純にインライン化してオブジェクトを手作業で構築するだけで、より多くのパフォーマンスを得ることができます。

JavaScriptトレースエンジンは、 for..inループを最適化するfor..inます。また、hasOwnPropertyをチェックすることで、あなたも遅くなります。 速度が絶対必要である場合、手動クローン。

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

DateオブジェクトのJSON.parse(JSON.stringify(obj))メソッドを使用することをJSON.parse(JSON.stringify(obj))くださいJSON.parse(JSON.stringify(obj)) JSON.stringify(new Date())は、 JSON.parse() DateオブジェクトにDateます。 詳細については、この回答を参照してください

さらに、少なくともChrome 65では、ネイティブクローニングは行かないことに注意してください。 このJSPerfによれば、新しい機能を作成してネイティブクローニングを実行することは、JSON.stringifyを使用するよりも800倍近く遅くなります。


これは非常にうまくいくライブラリ(「クローン」と呼ばれます)があります。 私が知っている任意のオブジェクトの最も完全な再帰的クローニング/コピーを提供します。 また、循環参照もサポートしていますが、これは他の回答ではカバーされていません。

あなたはnpmでもそれを見つけることができます 。 これはブラウザやNode.jsでも使用できます。

それを使用する方法の例はここにあります:

それをインストールする

npm install clone

Enderパッケージ化してください。

ender build clone [...]

また、ソースコードを手動でダウンロードすることもできます。

その後、ソースコードで使用することができます。

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

(免責事項:私は図書館の著者です。)


それを使用している場合、 Underscore.jsライブラリにはcloneメソッドがあります。

var newObject = _.clone(oldObject);

コード:

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

テスト:

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

上記のConroyPの答えは、コンストラクタにパラメータが必要な場合でも動作します:

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

この関数は、私のsimpleooライブラリでも利用できます。

編集:

より堅牢なバージョンです(Justin McCandlessのおかげで、これも循環参照をサポートしています):

/**
 * 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://.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();
    };
}

変数だけを持ち、オブジェクトに関数を持たないと仮定すると、次のように使うことができます:

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

私はあなたの目的が "普通の古いJavaScriptオブジェクト"を複製するかどうかによって2つの良い答えがあります。

また、元のオブジェクトにプロトタイプ参照がない完全なクローンを作成することを意図しているとしましょう。完全なクローンに興味がない場合は、他の答え(Crockfordのパターン)の中で提供されているObject.clone()ルーチンの多くを使用することができます。

普通の古いJavaScriptオブジェクトの場合、現代のランタイムでオブジェクトをクローン化するための実証済みの真の方法は非常に簡単です。

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

ソースオブジェクトは純粋なJSONオブジェクトでなければならないことに注意してください。つまり、そのネストされたプロパティはすべてブール値、文字列、配列、オブジェクトなどのスカラーでなければなりません。RegExpやDateのような関数や特別なオブジェクトは複製されません。

それは効率的ですか?はい、はい。あらゆる種類のクローニング方法を試しましたが、これは最も効果的です。私はいくつかの忍者がより速い方法を思いつくことができると確信しています。しかし私は限界利益について話していると思う。

このアプローチは簡単で実装が簡単です。便利な関数にラップしてください。実際にいくつかの利得を絞り出す必要がある場合は、後で実行してください。

さて、非普通のJavaScriptオブジェクトの場合、本当に簡単な答えはありません。実際には、JavaScript関数と内部オブジェクトの状態の動的性質のために存在することはありません。関数を含むJSON構造を深くクローンするには、それらの関数とその内部コンテキストを再作成する必要があります。JavaScriptには標準化された方法がありません。

これを行う正しい方法は、コード内で宣言して再利用する便利な方法です。便利なメソッドには、自分のオブジェクトをある程度理解しておくことで、新しいオブジェクト内のグラフを適切に再作成できるようにすることができます。

私たちは私たち自身で書かれていますが、私が見てきた最良の一般的なアプローチはここにあります:

http://davidwalsh.name/javascript-clone

これは正しい考えです。著者(David Walsh)は、一般化された関数のクローン作成についてコメントしています。これは、ユースケースに応じて、あなたがやりたいことがあるかもしれません。

主な考え方は、タイプごとに関数(またはプロトタイプクラス)のインスタンス化を特別に処理する必要があるということです。ここでは、RegExpとDateのいくつかの例を紹介しました。

このコードは簡潔であるだけでなく、非常に読みやすくなっています。それはかなり簡単に拡張することができます。

これは効率的ですか?はい、はい。真のディープコピークローンを生成することを目標とすると、ソースオブジェクトグラフのメンバーを歩かなければならなくなります。このアプローチでは、処理する子メンバーと、カスタムタイプを手動で処理する方法を正確に調整できます。

だからそこに行く。2つのアプローチ。どちらも効率的です。


JSON.parse(JSON.stringify(obj))バージョンを使用したいがDateオブジェクトを失うことのない人にとってはparsemethod2番目の引数を使って文字列をDateに戻すことができます:

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

Lodashには素敵なlodash.com/docs#cloneDeepメソッドがあります:

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

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

これは一般的に最も効率的な解決策ではありませんが、私が必要とするものを実行します。以下の簡単なテストケース...

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

サイクリックアレイテスト...

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

機能テスト...

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

ちょうど私がAngularJS言及していないのを見て、人々が知りたいかもしれないと思ったので...

angular.copy オブジェクトと配列を詳細にコピーする方法も提供します。


浅いコピー1ライナー(ECMAScript第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

そして浅いコピーの1ライナー(ECMAScript 6th edition、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

私はhere最高の投票here答えに同意しhere。再帰ディープクローンであるはるかに速くよりJSON.parse(JSON.stringify(OBJ))に述べたアプローチ。

クイックリファレンスの機能は次のとおりです。

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
}

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

function clone(obj)
 { var clone = {};
   clone.prototype = obj.prototype;
   for (property in obj) clone[property] = obj[property];
   return clone;
 }




clone