函数功能
源码
cleverMerge 函数中代码相对比较简单,主要的逻辑在 _cleverMerge 函数中。下面只做展示
const cleverMerge = (first, second) => {
if (second === undefined) return first;
if (first === undefined) return second;
if (typeof second !== "object" || second === null) return second;
if (typeof first !== "object" || first === null) return first;
return _cleverMerge(first, second, false);
};
函数功能
主要功能是对给定的对象进行结构化解析,并组织信息以便于后续的访问和处理
函数分析
const info = new Map();
const getInfo = p => {
const entry = info.get(p); // 获取p在info中对于的值
if (entry !== undefined) return entry; // 在info中存在p对于的值,就直接返回 entry
const newEntry = { // 否则就创建一个对象
base: undefined,
byProperty: undefined,
byValues: undefined
};
info.set(p, newEntry); // 并按照键值对的形式存入
return newEntry; // 返回新的对象
};
for (const key of Object.keys(obj)) {
if (key.startsWith("by")) {
const byProperty = key;
const byObj = obj[byProperty];
if (typeof byObj === "object") {
for (const byValue of Object.keys(byObj)) {
const obj = byObj[byValue];
for (const key of Object.keys(obj)) {
const entry = getInfo(key);
if (entry.byProperty === undefined) {
entry.byProperty = byProperty;
entry.byValues = new Map();
} else if (entry.byProperty !== byProperty) {
throw new Error(
`${byProperty} and ${entry.byProperty} for a single property is not supported`
);
}
entry.byValues.set(byValue, obj[key]);
if (byValue === "default") {
for (const otherByValue of Object.keys(byObj)) {
if (!entry.byValues.has(otherByValue))
entry.byValues.set(otherByValue, undefined);
}
}
}
}
} else if (typeof byObj === "function") {
if (dynamicInfo === undefined) {
dynamicInfo = {
byProperty: key,
fn: byObj
};
} else {
throw new Error(
`${key} and ${dynamicInfo.byProperty} when both are functions is not supported`
);
}
} else {
const entry = getInfo(key);
entry.base = obj[key];
}
} else {
const entry = getInfo(key);
entry.base = obj[key];
}
}
if (key.startsWith("by")) {
const byProperty = key; // 获取赋值当前 obj 的key给byProperty
const byObj = obj[byProperty]; // 并获取当前key对应的属性值赋值给byObj
if (typeof byObj === "object") { // 判断 byObj 的类型
for (const byValue of Object.keys(byObj)) { // 遍历对象
const obj = byObj[byValue]; // 获取对象内的值,这里的obj也是对象
for (const key of Object.keys(obj)) {
const entry = getInfo(key); // 通过 key 获取在info中对应的值
if (entry.byProperty === undefined) { // 如果为undefined
entry.byProperty = byProperty;
entry.byValues = new Map();
} else if (entry.byProperty !== byProperty) { // 两个不相同是不支持的
throw new Error(
`${byProperty} and ${entry.byProperty} for a single property is not supported`
);
}
entry.byValues.set(byValue, obj[key]);
if (byValue === "default") { // 为 default时
for (const otherByValue of Object.keys(byObj)) {
if (!entry.byValues.has(otherByValue)) // 判断当前entry下的byValues 中是否存在 当前otherByValue
entry.byValues.set(otherByValue, undefined);//存在时,设置为undefined
}
}
}
}
}
} else {
const entry = getInfo(key);
entry.base = obj[key];
}
}
这里byProperty、byObj、byValue和obj几个变量比较相近,如果没有实际案例作为参照不太好理解。下面做一个参照例子else if (typeof byObj === "function") { // 判断是否为函数
if (dynamicInfo === undefined) {
dynamicInfo = { // 设置动态信息
byProperty: key,
fn: byObj
};
}
}
else {
const entry = getInfo(key);
entry.base = obj[key];
}
else {
throw new Error(
`${key} and ${dynamicInfo.byProperty} when both are functions is not supported`
);
return {
static: info,
dynamic: dynamicInfo
};
案例分析
let resolve = {
byDependency: {
esm: {
mainFields: ['browser', 'module'],
},
commonjs: {
aliasFields: ['browser'],
},
url: {
preferRelative: true,
},
}
}
console.dir(parseObject(resolve), { depth: 10 });
/**
* @param {object} obj the object
* @returns {ParsedObject} parsed object
*/
const parseObject = obj => {
const info = new Map();
let dynamicInfo;
const getInfo = p => {
const entry = info.get(p);
if (entry !== undefined) return entry;
const newEntry = {
base: undefined,
byProperty: undefined,
byValues: undefined
};
info.set(p, newEntry);
return newEntry;
};
for (const key of Object.keys(obj)) {
if (key.startsWith("by")) {
const byProperty = key;
const byObj = obj[byProperty];
if (typeof byObj === "object") {
for (const byValue of Object.keys(byObj)) {
const obj = byObj[byValue];
for (const key of Object.keys(obj)) {
const entry = getInfo(key);
if (entry.byProperty === undefined) {
entry.byProperty = byProperty;
entry.byValues = new Map();
} else if (entry.byProperty !== byProperty) {
throw new Error(
`${byProperty} and ${entry.byProperty} for a single property is not supported`
);
}
entry.byValues.set(byValue, obj[key]);
if (byValue === "default") {
for (const otherByValue of Object.keys(byObj)) {
if (!entry.byValues.has(otherByValue))
entry.byValues.set(otherByValue, undefined);
}
}
}
}
} else if (typeof byObj === "function") {
if (dynamicInfo === undefined) {
dynamicInfo = {
byProperty: key,
fn: byObj
};
} else {
throw new Error(
`${key} and ${dynamicInfo.byProperty} when both are functions is not supported`
);
}
} else {
const entry = getInfo(key);
entry.base = obj[key];
}
} else {
const entry = getInfo(key);
entry.base = obj[key];
}
}
return {
static: info,
dynamic: dynamicInfo
};
};
函数功能
主要功能是解析给定的对象并缓存结果以提高后续解析性能
函数分析
const parseCache = new WeakMap();
const entry = parseCache.get(obj);
if (entry !== undefined) return entry;
const result = parseObject(obj);
parseCache.set(obj, result);
源码
const parseCache = new WeakMap();
/**
* @param {object} obj the object
* @returns {ParsedObject} parsed object
*/
const cachedParseObject = obj => {
const entry = parseCache.get(obj);
if (entry !== undefined) return entry;
const result = parseObject(obj);
parseCache.set(obj, result);
return result;
};
函数功能
主要功能是合并两个给定的对象,并且使用缓存来存储合并结果,以便在将来传入相同的对象时可以直接返回缓存的结果,而不需要重新计算。
函数前半部分判断和cleverMerge 函数中的一致,如下
if (second === undefined) return first;
if (first === undefined) return second;
if (typeof second !== "object" || second === null) return second;
if (typeof first !== "object" || first === null) return first;
函数分析
const mergeCache = new WeakMap();
let innerCache = mergeCache.get(first);
if (innerCache === undefined) {
innerCache = new WeakMap();
mergeCache.set(first, innerCache);
}
const prevMerge = innerCache.get(second);
if (prevMerge !== undefined) return prevMerge;
const newMerge = _cleverMerge(first, second, true);
innerCache.set(second, newMerge);
return newMerge;
源码
const mergeCache = new WeakMap();
const cachedCleverMerge = (first, second) => {
if (second === undefined) return first;
if (first === undefined) return second;
if (typeof second !== "object" || second === null) return second;
if (typeof first !== "object" || first === null) return first;
let innerCache = mergeCache.get(first);
if (innerCache === undefined) {
innerCache = new WeakMap();
mergeCache.set(first, innerCache);
}
const prevMerge = innerCache.get(second);
if (prevMerge !== undefined) return prevMerge;
const newMerge = _cleverMerge(first, second, true);
innerCache.set(second, newMerge);
return newMerge;
};
函数功能
该函数的功能是判断并返回传入值的类型。它根据传入不同的值。返回不同类型。
函数分析
if (value === undefined) {
return VALUE_TYPE_UNDEFINED;
}
else if (value === DELETE) {
return VALUE_TYPE_DELETE;
}
else if (Array.isArray(value)) {
if (value.lastIndexOf("...") !== -1) return VALUE_TYPE_ARRAY_EXTEND;
return VALUE_TYPE_ATOM;
}
else if (
typeof value === "object" &&
value !== null &&
(!value.constructor || value.constructor === Object)
) {
return VALUE_TYPE_OBJECT;
}
源码
const VALUE_TYPE_UNDEFINED = 0;
const VALUE_TYPE_ATOM = 1;
const VALUE_TYPE_ARRAY_EXTEND = 2;
const VALUE_TYPE_OBJECT = 3;
const VALUE_TYPE_DELETE = 4;
/**
* @param {any} value a single value
* @returns {VALUE_TYPE_UNDEFINED | VALUE_TYPE_ATOM | VALUE_TYPE_ARRAY_EXTEND | VALUE_TYPE_OBJECT | VALUE_TYPE_DELETE} value type
*/
const getValueType = value => {
if (value === undefined) {
return VALUE_TYPE_UNDEFINED;
} else if (value === DELETE) {
return VALUE_TYPE_DELETE;
} else if (Array.isArray(value)) {
if (value.lastIndexOf("...") !== -1) return VALUE_TYPE_ARRAY_EXTEND;
return VALUE_TYPE_ATOM;
} else if (
typeof value === "object" &&
value !== null &&
(!value.constructor || value.constructor === Object)
) {
return VALUE_TYPE_OBJECT;
}
return VALUE_TYPE_ATOM;
};
函数功能
mergeEntries 函数的主要功能是合并两个对象,这些对象可能包含基础值、按属性分类的值,以及按值分类的映射
函数分析
switch (getValueType(secondEntry.base)) {
}
case VALUE_TYPE_ATOM:
case VALUE_TYPE_DELETE:
// No need to consider firstEntry at all
// second value override everything
// = second.base + second.byProperty
return secondEntry;
if (!firstEntry.byProperty) {
// = first.base + second.byProperty
return {
base: firstEntry.base,
byProperty: secondEntry.byProperty,
byValues: secondEntry.byValues
};
}
else if (firstEntry.byProperty !== secondEntry.byProperty) {
throw new Error(
`${firstEntry.byProperty} and ${secondEntry.byProperty} for a single property is not supported`
);
}
else {
// = first.base + (first.byProperty + second.byProperty)
// need to merge first and second byValues
const newByValues = new Map(firstEntry.byValues);
for (const [key, value] of secondEntry.byValues) {
const firstValue = getFromByValues(firstEntry.byValues, key);
newByValues.set(
key,
mergeSingleValue(firstValue, value, internalCaching)
);
}
return {
base: firstEntry.base,
byProperty: firstEntry.byProperty,
byValues: newByValues
};
}
default: {
if (!firstEntry.byProperty) {
// The simple case
// = (first.base + second.base) + second.byProperty
return {
base: mergeSingleValue(
firstEntry.base,
secondEntry.base,
internalCaching
),
byProperty: secondEntry.byProperty,
byValues: secondEntry.byValues
};
}
let newBase;
const intermediateByValues = new Map(firstEntry.byValues);
for (const [key, value] of intermediateByValues) {
intermediateByValues.set(
key,
mergeSingleValue(value, secondEntry.base, internalCaching)
);
}
if (
Array.from(firstEntry.byValues.values()).every(value => {
const type = getValueType(value);
return type === VALUE_TYPE_ATOM || type === VALUE_TYPE_DELETE;
})
) {
// = (first.base + second.base) + ((first.byProperty + second.base) + second.byProperty)
newBase = mergeSingleValue(
firstEntry.base,
secondEntry.base,
internalCaching
);
} else {
// = first.base + ((first.byProperty (+default) + second.base) + second.byProperty)
newBase = firstEntry.base;
if (!intermediateByValues.has("default"))
intermediateByValues.set("default", secondEntry.base);
}
if (!secondEntry.byProperty) {
// = first.base + (first.byProperty + second.base)
return {
base: newBase,
byProperty: firstEntry.byProperty,
byValues: intermediateByValues
};
} else if (firstEntry.byProperty !== secondEntry.byProperty) {
throw new Error(
`${firstEntry.byProperty} and ${secondEntry.byProperty} for a single property is not supported`
);
}
const newByValues = new Map(intermediateByValues);
for (const [key, value] of secondEntry.byValues) {
const firstValue = getFromByValues(intermediateByValues, key);
newByValues.set(
key,
mergeSingleValue(firstValue, value, internalCaching)
);
}
return {
base: newBase,
byProperty: firstEntry.byProperty,
byValues: newByValues
};
}
源码
/**
* @param {ObjectParsedPropertyEntry} firstEntry a
* @param {ObjectParsedPropertyEntry} secondEntry b
* @param {boolean} internalCaching should parsing of objects and nested merges be cached
* @returns {ObjectParsedPropertyEntry} new entry
*/
const mergeEntries = (firstEntry, secondEntry, internalCaching) => {
switch (getValueType(secondEntry.base)) {
case VALUE_TYPE_ATOM:
case VALUE_TYPE_DELETE:
// No need to consider firstEntry at all
// second value override everything
// = second.base + second.byProperty
return secondEntry;
case VALUE_TYPE_UNDEFINED:
if (!firstEntry.byProperty) {
// = first.base + second.byProperty
return {
base: firstEntry.base,
byProperty: secondEntry.byProperty,
byValues: secondEntry.byValues
};
} else if (firstEntry.byProperty !== secondEntry.byProperty) {
throw new Error(
`${firstEntry.byProperty} and ${secondEntry.byProperty} for a single property is not supported`
);
} else {
// = first.base + (first.byProperty + second.byProperty)
// need to merge first and second byValues
const newByValues = new Map(firstEntry.byValues);
for (const [key, value] of secondEntry.byValues) {
const firstValue = getFromByValues(firstEntry.byValues, key);
newByValues.set(
key,
mergeSingleValue(firstValue, value, internalCaching)
);
}
return {
base: firstEntry.base,
byProperty: firstEntry.byProperty,
byValues: newByValues
};
}
default: {
if (!firstEntry.byProperty) {
// The simple case
// = (first.base + second.base) + second.byProperty
return {
base: mergeSingleValue(
firstEntry.base,
secondEntry.base,
internalCaching
),
byProperty: secondEntry.byProperty,
byValues: secondEntry.byValues
};
}
let newBase;
const intermediateByValues = new Map(firstEntry.byValues);
for (const [key, value] of intermediateByValues) {
intermediateByValues.set(
key,
mergeSingleValue(value, secondEntry.base, internalCaching)
);
}
if (
Array.from(firstEntry.byValues.values()).every(value => {
const type = getValueType(value);
return type === VALUE_TYPE_ATOM || type === VALUE_TYPE_DELETE;
})
) {
// = (first.base + second.base) + ((first.byProperty + second.base) + second.byProperty)
newBase = mergeSingleValue(
firstEntry.base,
secondEntry.base,
internalCaching
);
} else {
// = first.base + ((first.byProperty (+default) + second.base) + second.byProperty)
newBase = firstEntry.base;
if (!intermediateByValues.has("default"))
intermediateByValues.set("default", secondEntry.base);
}
if (!secondEntry.byProperty) {
// = first.base + (first.byProperty + second.base)
return {
base: newBase,
byProperty: firstEntry.byProperty,
byValues: intermediateByValues
};
} else if (firstEntry.byProperty !== secondEntry.byProperty) {
throw new Error(
`${firstEntry.byProperty} and ${secondEntry.byProperty} for a single property is not supported`
);
}
const newByValues = new Map(intermediateByValues);
for (const [key, value] of secondEntry.byValues) {
const firstValue = getFromByValues(intermediateByValues, key);
newByValues.set(
key,
mergeSingleValue(firstValue, value, internalCaching)
);
}
return {
base: newBase,
byProperty: firstEntry.byProperty,
byValues: newByValues
};
}
}
};
因篇幅问题不能全部显示,请点此查看更多更全内容