高级前端
手写代码
【Q489】如何实现一个函数 lodash.merge

如何实现一个函数 lodash.merge

Issue 欢迎在 Gtihub Issue 中回答此问题: Issue 498 (opens in a new tab)

Author 回答者: shfshanyue (opens in a new tab)

Array.prototype.flatMap 已经是 EcmaScript 的标准,看一个例子,它的输出是多少?

[1, 2, [3], 4].flatMap((x) => x + 1);
//=> [2, 3, '31', 5]

很可惜,不是 [2, 3, 4, 5],原因在于 flatMap 实际上是先 mapflat,实现如下

Array.prototype.flatMap = function (mapper) {
  return this.map(mapper).flat();
};

Author 回答者: hwb2017 (opens in a new tab)

const getRawType = (val) => {
  return Object.prototype.toString.call(val).slice(8, -1);
};
const isPlainObject = (val) => {
  return getRawType(val) === "Object";
};
 
const isPlainObjectOrArray = (val) => {
  return isPlainObject(val) || Array.isArray(val);
};
 
const merge = (object, ...sources) => {
  for (const source of sources) {
    for (const key in source) {
      if (source[key] === undefined && key in object) {
        continue;
      }
      if (isPlainObjectOrArray(source[key])) {
        if (
          isPlainObjectOrArray(object[key]) &&
          getRawType(object[key]) === getRawType(source[key])
        ) {
          if (isPlainObject(object[key])) {
            merge(object[key], source[key]);
          } else {
            object[key] = object[key].concat(source[key]);
          }
        } else {
          object[key] = source[key];
        }
      } else {
        object[key] = source[key];
      }
    }
  }
};
 
// merge array
var object = {
  a: [{ b: 2 }, { d: 4 }],
};
merge(object, { a: [{ c: 3 }, { e: 5 }] });
console.log(object);
 
// merge object
var object = {
  a: { b: { c: 1 } },
};
merge(object, { a: { b: { d: 2 } } });
console.log(object);
 
// overwrite primitive value
object = {
  a: { b: 1 },
};
merge(object, { a: { b: 2 } });
console.log(object);
 
// skip undefined
object = {
  a: { b: 1 },
};
merge(object, { a: { b: undefined } });
console.log(object);
 
// multiple sources
var object = {
  a: { b: { c: 1, d: [1] } },
};
merge(object, { a: { b: { e: 2 } } }, { a: { b: { d: [2] } } });
console.log(JSON.stringify(object));