これはES6でオブジェクトを複製する良い方法ですか?


155

"javascript clone object"のグーグルは、いくつかの本当に奇妙な結果をもたらします、それらのいくつかは絶望的に時代遅れであり、いくつかはあまりに複雑すぎます。

let clone = {...original};

これに問題はありますか?


1
これは正当なES6ではありません。しかし、それが行われた場合、これはクローンではありません。現在、クローンと元のプロパティの両方が同じものを指しています。たとえば、original = { a: [1,2,3] }clone.a文字通りのクローンを提供しますoriginal.a。いずれcloneかによるoriginal変更または同じものを変更するため、いいえ、これは悪い=)
Mike 'Pomax' Kamermans

2
@AlbertoRiveraそれのちょっと有効なJavaScriptが、その中でそれはだ、ステージ2のJavaScript標準への将来加算される可能性が高いの提案。
Frxstrem 2016

ES6に関する質問の@Frxstrem、これは無効なJavaScript =)
Mike 'Pomax' Kamermans

3
浅いまたは深いクローニング?
Felix Kling 2016

2
そうです、それは有効なES6ではなく、有効なES9です。developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
mikemaccana

回答:


240

これは浅いクローニングに適しています。オブジェクトの広がりは、ECMAScriptの2018の標準的な部分です

ディープクローンを作成するには、別のソリューションが必要です。

const clone = {...original} 浅いクローンに

const newobj = {...original, prop: newOne} オリジナルに別のプロップを不変に追加し、新しいオブジェクトとして保存します。


18
しかし、これは浅いクローンではないのですか?のように、プロパティは再帰的に複製されませんか?したがって、original.innerObject === clone.innerObjectで、original.innerObject.propertyを変更すると、clone.innerObject.propertyも変更されます。
milanio

18
はい、これは浅いクローンです。ディープクローンが必要な場合は、使用する必要がありますJSON.parse(JSON.stringify(input))
M.academyのMark Shust

8
/!\ JSON.parse(JSON.stringify(input))は日付をめちゃくちゃにする、未定義、...クローン作成の特効薬ではありません!参照:maxpou.fr/immutability-js-without-library
ギョーム

1
それで、ハックJSON.stringify()/ JSON.parse()は、ES6でオブジェクトをディープクローンするための推奨される方法ですか?私はそれが推奨されるのを見続けます。不安。
Solvitieg 2018年

3
@MarkShust JSON.parse(JSON.stringify(input))は機能しません。存在する場合、functionsまたはinfinity値として存在 する場合は、単にnullその場所に割り当てられるためです。値が単純な場合literalsとそうでない場合にのみ機能しますfunctions
backslashN

65

編集:この回答が投稿されたとき{...obj}、ほとんどのブラウザでは構文が利用できませんでした。今日では、IE 11をサポートする必要がない限り、問題なく使用できます。

Object.assignを使用します。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

var obj = { a: 1 };
var copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }

ただし、これではディープクローンは作成されません。現在のところ、ディープクローニングのネイティブな方法はまだありません。

編集:@Mike 'Pomax' Kamermansがコメントで述べたように、以下を使用して単純なオブジェクト(つまり、プロトタイプ、関数、または循環参照なし)をディープクローンできます。 JSON.parse(JSON.stringify(input))


19
オブジェクトが真のオブジェクトリテラルであり、純粋にデータである場合JSON.parse(JSON.stringify(input))は、適切なディープクローンが1つあります。ただし、プロトタイプ、関数、または循環参照が機能している瞬間、そのソリューションは機能しなくなります。
Mike 'Pomax' Kamermans 2016

@ Mike'Pomax'Kamermansそうですね。ゲッターとセッターの機能を失うことはひどいですが...
Alberto Rivera '28

オブジェクトをディープクローンする一般的な機能が必要な場合は、stackoverflow.com / a / 13333781/560114を確認してください。
Matt Browne 2016


1
@DanDascalescuそれは実験的ですが、かなり有望に見えます。情報をありがとう!
Alberto Rivera

4

使用したメソッドがDateなどのデータ型を含むオブジェクトでうまく機能しない場合は、これを試してください

インポート _

import * as _ from 'lodash';

ディープクローンオブジェクト

myObjCopy = _.cloneDeep(myObj);

ちょうどimport _ from 'lodash';十分です。しかし、「ホイールを再発明しないでください」という答えの+1。
rustyx

ロダッシュは肥大化しています。単純なディープコピーのためだけにlodashを取り込む必要はありません。ここには他の多くのソリューションがあります。これは、無駄のないアプリを構築しようとしているWeb開発者にとって本当に悪い答えです。
Jason Rice

3

json.parse(json.stringify(object))を使用したくない場合は、再帰的にキーと値のコピーを作成できます。

function copy(item){
  let result = null;
  if(!item) return result;
  if(Array.isArray(item)){
    result = [];
    item.forEach(element=>{
      result.push(copy(element));
    });
  }
  else if(item instanceof Object && !(item instanceof Function)){ 
    result = {};
    for(let key in item){
      if(key){
        result[key] = copy(item[key]);
      }
    }
  }
  return result || item;
}

しかし、最善の方法は、自身のクローンを返すことができるクラスを作成することです

class MyClass{
    data = null;
    constructor(values){ this.data = values }
    toString(){ console.log("MyClass: "+this.data.toString(;) }
    remove(id){ this.data = data.filter(d=>d.id!==id) }
    clone(){ return new MyClass(this.data) }
}

2

@marcelの回答に続いて、クローンされたオブジェクトで一部の関数がまだ見つからないことがわかりました。例えば

function MyObject() {
  var methodAValue = null,
      methodBValue = null

  Object.defineProperty(this, "methodA", {
    get: function() { return methodAValue; },
    set: function(value) {
      methodAValue = value || {};
    },
    enumerable: true
  });

  Object.defineProperty(this, "methodB", {
    get: function() { return methodAValue; },
    set: function(value) {
      methodAValue = value || {};
    }
  });
}

MyObjectではmethodAを複製できましたが、methodBは除外されました。これは欠落しているために発生しました

enumerable: true

それはそれが現れなかったことを意味しました

for(let key in item)

代わりに私はに切り替えました

Object.getOwnPropertyNames(item).forEach((key) => {
    ....
  });

列挙できないキーが含まれます。

また、プロトタイプ(proto)が複製されていないこともわかりました。そのため、私は結局使用しました

if (obj.__proto__) {
  copy.__proto__ = Object.assign(Object.create(Object.getPrototypeOf(obj)), obj);
}

PS:これを行うための組み込み関数を見つけることができなかったことに不満を感じています。


1

こうすることもできます

let copiedData = JSON.parse(JSON.stringify(data));

-1
We can do that with two way:
1- First create a new object and replicate the structure of the existing one by iterating 
 over its properties and copying them on the primitive level.

let user = {
     name: "John",
     age: 30
    };

    let clone = {}; // the new empty object

    // let's copy all user properties into it
    for (let key in user) {
      clone[key] = user[key];
    }

    // now clone is a fully independant clone
    clone.name = "Pete"; // changed the data in it

    alert( user.name ); // still John in the original object

2- Second we can use the method Object.assign for that 
    let user = { name: "John" };
    let permissions1 = { canView: true };
    let permissions2 = { canEdit: true };

    // copies all properties from permissions1 and permissions2 into user
    Object.assign(user, permissions1, permissions2);

  -Another example

    let user = {
      name: "John",
      age: 30
    };

    let clone = Object.assign({}, user);
It copies all properties of user into the empty object and returns it. Actually, the same as the loop, but shorter.

しかし、Object.assign()は深いクローンを作成しません

let user = {
  name: "John",
  sizes: {
    height: 182,
    width: 50
  }
};

let clone = Object.assign({}, user);

alert( user.sizes === clone.sizes ); // true, same object

// user and clone share sizes
user.sizes.width++;       // change a property from one place
alert(clone.sizes.width); // 51, see the result from the other one

これを修正するには、user [key]の各値を調べる複製ループを使用し、それがオブジェクトの場合は、その構造も複製する必要があります。これは「ディープクローニング」と呼ばれます。

上記のケースとより複雑なケースを処理するディープクローニングの標準アルゴリズムがあり、構造化クローニングアルゴリズムと呼ばれます。車輪の再発明をしないようにするために、我々は、JavaScriptライブラリからの作業の実装を使用することができますlodashメソッドが呼び出され_.cloneDeep(OBJ)


-1

上記のすべてのメソッドは、nレベルにネストされているオブジェクトのディープクローニングを処理しません。私は他の人と比べてそのパフォーマンスをチェックしませんでしたが、それは短くてシンプルです。

以下の最初の例はObject.assign、最初のレベルまでのクローンを使用したオブジェクトのクローン作成を示しています。

var person = {
    name:'saksham',
    age:22,
    skills: {
        lang:'javascript',
        experience:5
    }
}

newPerson = Object.assign({},person);
newPerson.skills.lang = 'angular';
console.log(newPerson.skills.lang); //logs Angular

以下のアプローチを使用して、ディープクローンオブジェクト

var person = {
    name:'saksham',
    age:22,
    skills: {
        lang:'javascript',
        experience:5
    }
}

anotherNewPerson = JSON.parse(JSON.stringify(person));
anotherNewPerson.skills.lang = 'angular';
console.log(person.skills.lang); //logs javascript


JSON.parse / stringifyは、何年もの間、不十分なディープクローニングメソッドとして言及されてきました。以前の回答と関連する質問を確認してください。また、これはES6の新機能ではありません。
Dan Dascalescu

@DanDascalescu私はこれを知っており、単純なオブジェクトに使用することは問題ではないと思います。他の人も、同じ投稿のコメントで、さらにはコメントとして、これについて言及しています。反対票に値しないと思います。
Saksham

正確に-「他の人も言及した」JSON.parse / stringifyの回答。なぜ同じ解決策でさらに別の回答を投稿するのですか?
Dan Dascalescu
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.