import isArray from './isArray';
import isObject from './isObject';
import isObjectLike from './isObjectLike';
import isPlainObject from './isPlainObject';
import { nativeUndefined } from './internals/native';
import allKeys from './allKeys';
// 内部处理合并和循环引用
function baseMerge(object, source, getKeys, customizer, stack = new WeakMap()) {
const obj = Object(object);
if (!isObject(source) || obj === source) {
return obj;
}
const keys = getKeys(source);
const hasCustomizer = typeof customizer === 'function';
keys.forEach((key) => {
// @ts-ignore
const srcValue = source[key];
const srcIsObj = isObject(srcValue);
if (srcIsObj && stack.has(srcValue)) {
obj[key] = srcValue;
}
else {
const newValue = hasCustomizer ? customizer(obj[key], srcValue, key, obj, source) : nativeUndefined;
if (newValue !== nativeUndefined) {
obj[key] = newValue;
}
else {
const objValue = obj[key];
let newObjValue;
if (srcIsObj) {
stack.set(srcValue, true);
if (isArray(srcValue)) {
newObjValue = isArray(objValue) ? objValue : [];
}
else if (isPlainObject(srcValue)) {
newObjValue = isObjectLike(objValue) ? objValue : {};
}
}
if (newObjValue) {
obj[key] = baseMerge(newObjValue, srcValue, getKeys, customizer, stack);
}
else if (srcValue !== nativeUndefined || !(key in obj)) {
obj[key] = srcValue;
}
}
}
});
return obj;
}
/**
* 递归合并 `source` 来源对象自身的可枚举属性(包含 `Symbol` 属性)到 `object` 目标对象。
*
* 如果目标值存在,被解析为 `undefined` 的 `source` 来源对象属性将被跳过。数组和普通对象会递归合并,其他对象和值会被直接分配覆盖。
*
* 如果不需要合并数组,第三个参数传入 `merge.NOT_MERGE_ARRAYS` 。
*
* 如果需要合并继承的属性,第四个参数传入 {@link https://caijf.github.io/ut2/module-Object.html#.allKeysIn allKeysIn} 方法, `merge(object, source, undefined, allKeysIn)`。
*
* @alias module:Object.merge
* @since 1.0.0
* @param {Object | Array} object 目标对象。
* @param {Object | Array} source 来源对象。
* @param {Function} [customizer] 自定义赋值函数,如果函数返回 `undefined` 将使用默认合并。
* @param {Function} [getKeys=allKeys] 自定义获取对象键方法。默认 `allKeys`。
* @returns {Object} 目标对象。
* @example
*
* merge({c: 3}, {e: 5}); // { c: 3, e: 5 }
* merge({ a: 1 }, { a: undefined, b: undefined }); // { a: 1, b: undefined }
*
* const object = {
* a: [{b: 2}, {d: 4}]
* }
* const other = {
* a: [{c: 3},{e: 5}]
* }
*
* merge(object, other); // { a: [{b: 2, c: 3}, {d: 4, e: 5}] }
*
* // 数组不合并
* merge(object, other, merge.NOT_MERGE_ARRAYS); // { a: [{c: 3},{e: 5}] }
*
* // 或自定义数组不合并方法
* merge(object, other, (objValue, srcValue) => isArray(srcValue) ? srcValue : undefined); // { a: [{c: 3},{e: 5}] }
*
*/
function merge(object, source, customizer, getKeys = allKeys) {
return baseMerge(object, source, getKeys, customizer);
}
merge.NOT_MERGE_ARRAYS = (objValue, srcValue) => (isArray(srcValue) ? srcValue : undefined);
export default merge;