Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
3
Help us understand the problem. What is going on with this article?
@norami_dream

JavascriptでObjectをextend / assign / mergeする4つの方法

More than 3 years have passed since last update.

何が問題か

JavascriptでObjectを拡張する際、Object.assignを使うことが多いと思いますが、レガシーなライブラリにはjQuery.extend, angular.extend等、似たようなものがいっぱいあります。そして、少しずつ仕様が違うため、古いコードのメンテナンス等の際には、混乱することがあります。そこで、レガシーなライブラリのextend / assign系の機能の違いを整理してみました。

1. Object階層:たどらない / prototype chain: たどらない

Object.assign
lodash.assign
angular.extend (Angular1.xのみ)

疑似コード

function assign(base, ...args){
  for(obj of args){
      for(const key in obj) {
      if(obj.hasOwnProperty(key){
              base[key] = obj[key];
          }  
      }
  }
  return base;
}

2. Object階層:たどらない / prototype chain: たどる

lodash.assignIn
jQuery.extend

疑似コード

function assignIn(base, ...args){
  for(obj of args){
      for(const key in obj) {
          base[key] = obj[key];
      }
  }
  return base;
}

3. Object階層:たどる / prototype chain: たどらない

lodash.merge
angular.merge (Angular1.xのみ)

解説

この場合、各種オブジェクトをどう扱うかが問題になります。angularの場合、Date, RegExp, DOMElement, Arrayに対応していて、それぞれ適切な形に変換されます。これ以外の一般的なオブジェクト(自分で作ったクラスなど)は、Plain Objectになってしまいます。lodashは調べていません。ちなみに、自分で作ったクラスの__proto__も適切にコピーするためには、以下の擬似コードのような実装が適切です。ただし、組み込みオブジェクトは、別に扱う必要があり、DateとRegExp以外の組み込みオブジェクトには対応していません。

余談ですが、angular.mergeは、deprecatedで、loash.mergeを使えとドキュメントに書いてあります。個人的には、なんでangular.extendがdeprecatedにならないのかが不思議です。

疑似コード

function merge(base, ...args) {
  for(const obj of args){
    if(typeof obj == 'object' && (obj instanceof Date || obj instanceof RegExp)) {
      base = new obj.__proto__.constructor(obj)
      continue;
    }
    if(typeof base != 'object') {
      base = Object.create(obj.__proto__)
    }
    for(const key in obj){
      if(obj.hasOwnProperty(key)){
        if(typeof obj[key] == 'object'){
          base[key] = merge(base[key], obj[key])
        }else {
          base[key] = obj[key];            
        }
      }
    }
  }
  return base;
}

4. Object階層:たどる / prototype chain: たどる

jQuery.extend(true)

解説

対応しているオブジェクトは、Arrayのみで、それ以外はPlain Objectになってしまいます。

まとめ

オブジェクトを拡張 / マージする際には、

  1. Objectの階層をたどるかどうか
  2. キーの列挙の際に、prototype chain をたどってコピーするか、
  3. __proto__をコピーするか、コピーせず、Plain Objectに変換するか
  4. 組み込みオブジェクトのコピーにどこまで対応するか

といった複雑な要素が絡み合っています。これらを考慮して、適切なライブラリを選択する必要があります。

多くの場合は、Object.assignとlodash.mergeで問題ないと思いますが、古いのコードの移植の際などは、機械的に変換してしまったりすると、痛い目遭いますので、くれぐれも注意してください。

3
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
norami_dream
Javascript, Python, 機械学習, Node.js, React, Vue

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
3
Help us understand the problem. What is going on with this article?