Encapsulation of data type detection methods
Before we talk about deep and shallow merges of objects, let’s encapsulate a few data type detection methods for later use. All of the following encapsulation methods are described in the JQuery Source Analysis – Data type detection method encapsulation (number, object, array class array). I won’t go into that here.
- Generic data type detection toType
var class2type = {};
var toString = class2type.toString;
var mapType = ["Boolean"."Number"."String"."Function"."Array"."Date"."RegExp"."Object"."Error"."Symbol"];
mapType.forEach(function(name){
class2type["[object "+name+"]"] = name.toLowerCase();
});
function toType(obj){
if(obj == null) {
return obj + "";
};
return typeof obj === "object" || typeof obj === "function" ?
class2type[toString.call(obj)] || "object" : typeof obj;
}
Copy the code
- Checks for pure isPlainObject
function isPlainObject(obj){
var proto, Ctor;
if(! obj ||Object.prototype.toString.call(obj) ! = ="[object Object]") {return false;
}
proto = Object.getPrototypeOf(obj);
if(! proto)return true;
Ctor = Object.prototype.hasOwnProperty.call(proto, "constructor") && proto.constructor;
return typeof Ctor === "function" && Function.prototype.toString.call(Ctor) === Function.prototype.toString.call(Object);
}
Copy the code
Shallow merge of objects
Shallow merging of objects means that when two objects are merged, each item in one object is replaced by the other object, and only the first layer is replaced regardless of whether there are any child objects in the object. Such as:
let obj1 = {
name:'Alvin'.age: 18.friends: {name:'Joe'.age: 18.friends2: {
name:'bill'.age: 20}}}let obj2 = {
name: 'Yannis'.age: 30.sex: 'woman'.friends: {name: 'Alvin'.sex: 'male'.friends2: {sex: 'woman'}}}Copy the code
In a shallow merge of the above two objects, every key and value in obj2 will be replaced by obj1, but in a shallow merge, only the key and value in the first level will be merged and replaced, and the other levels will not be merged but replaced directly. Law of merging:
- Obj1 and Obj2 are both objects, so iterate over OBJ2 to replace obj1 in turn (but only at the first level)
- If obj1 is not an object and obj2 is an object, replace obj1 with obj2
- If obj1 is an object and obj2 is not an object, obj1 prevails
- If neither obj1 nor obj2 is an object, replace obj1 with obj2
The combined result in the above code is:
let objMerge = {
name: 'Yannis'.// Replace the value in obj1
age: 30.// Replace the value in obj1
sex: 'woman'.// Merge existing keys and values in obj2
friends: {// Replace the existing keys and values in obj2
name: 'Alvin'.sex: 'male'.friends2: {// Replace the existing keys and values in obj2
sex: 'woman'}}}Copy the code
- Object shallow merge code implementation:
function shallowMerge(obj1, obj2){
var isPlain1 = isPlainObject(obj1);
var isPlain2 = isPlainObject(obj2);
// As long as obj1 is not an object, replace obj1 directly with obj2 regardless of whether obj2 is an object
if(! isPlain1)return obj2;
// At this point, obj1 must be an object. If obj2 is not an object, then obj1 will prevail
if(! isPlain2)return obj1;
// If neither of the above conditions is true, then obj1 and Obj2 must both be objects
let keys = [
...Object.keys(obj2),
...Object.getOwnPropertySymbols(obj2)
]
keys.forEach(function(key){
obj1[key] = obj2[key];
});
return obj1;
}
Copy the code
Deep merge of objects
Through our understanding of shallow merge above, I believe there are almost some concepts of deep merge. Deep merge is to replace and merge the keys and values of each level of the object according to the rules of shallow merge, no matter how many levels in the object, will recursively merge. Law of merging:
- Obj1 and Obj2 are both objects, so iterate over OBJ2 to replace OBJ1 (at each level)
- If obj1 is not an object and obj2 is an object, replace obj1 with obj2
- If obj1 is an object and obj2 is not an object, obj1 prevails
- If neither obj1 nor obj2 is an object, replace obj1 with obj2
Again, the result of the deep merge of the above code is:
let objMerge = {
name: 'Yannis'.// Replace the value in obj1
age: 30.// Replace the value in obj1
sex: 'woman'.// Merge existing keys and values in obj2
friends: {// Continue to merge
name: 'Alvin'.// Merge the values from obj2
age: 18.// The original value of obj1
sex: 'male'.// Merge the values from obj2
friends2: {// Continue to merge
name:'bill'.// The original value of obj1
age: 20.// The original value of obj1
sex: 'woman'// Merge the values from obj2}}}Copy the code
- Object deep merge code implementation:
function deepMerge(obj1, obj2){
var isPlain1 = isPlainObject(obj1);
var isPlain2 = isPlainObject(obj2);
// If either obj1 or Obj2 is not an object, the shallow merge rule is followed
if(! isPlain1 || ! isPlain2)return shallowMerge(obj1, obj2);
// If both are objects, then recursively merge each level
let keys = [
...Object.keys(obj2),
...Object.getOwnPropertySymbols(obj2)
]
keys.forEach(function(key){
obj1[key] = deepMerge(obj1[key], obj2[key]);// This is called recursively
});
return obj1;
}
Copy the code
Let obj = {name:’louyaqian’}; let obj = {name:’louyaqian’}; obj.obj1 = obj; To prevent this infinite loop, we need to add some logic
function deepMerge(obj1, obj2, cache){
// To prevent looping, we need to add the looping objects to the array
cache = !Array.isArray(cache) ? [] : cache;
If obj2 has already been compared and merged, return to obj2. Otherwise, continue merging
if(cache.indexOf(obj2)) return obj2;
cache.push(obj2);
var isPlain1 = isPlainObject(obj1);
var isPlain2 = isPlainObject(obj2);
// If either obj1 or Obj2 is not an object, the shallow merge rule is followed
if(! isPlain1 || ! isPlain2)return shallowMerge(obj1, obj2);
// If both are objects, then recursively merge each level
let keys = [
...Object.keys(obj2),
...Object.getOwnPropertySymbols(obj2)
]
keys.forEach(function(key){
obj1[key] = deepMerge(obj1[key], obj2[key], cache);// This is called recursively
});
return obj1;
}
Copy the code