function mergeOne(dst, src) {
    if (src === undefined) return dst;

    // According to specification arrays must be concatenated.
    // Create a new array instance. Overrides the 'dst'.
    if (Array.isArray(src)) {
      if (Array.isArray(dst)) return [...dst, ...src];
      return [...src]; // ignore the 'dst'
    }
    // Now deal with non plain 'src' object. 'src' overrides 'dst'
    // Note that functions are also assigned! We do not deep merge functions.
    if (!isPlainObject(src)) return src;

    for (const key of getOwnPropertyKeys(src)) {
      const desc = Object.getOwnPropertyDescriptor(src, key);
      if (desc.hasOwnProperty("value")) {
        // is this a regular property?
        // Do not merge properties with the 'undefined' value.
        if (desc.value !== undefined) {
          // deep merge each property. Recursion!
          dst[key] = mergeOne(
            isPlainObject(dst[key]) || Array.isArray(src[key]) ? dst[key] : {},
            src[key],
          );
        }
      } else {
        // nope, it looks like a getter/setter
        // Make it rewritable because two stamps can have same named getter/setter
        Object.defineProperty(dst, key, desc);
      }
    }

    return dst;
  }
Unlike _.merge(), our merge() copies symbols, getters and setters.
The 'src' argument plays the command role.
The returned values is always of the same type as the 'src'.
@param {Array|Object|*} dst Destination
@param {Array|Object|*} src Source
@returns {Array|Object|*} The `dst` argument