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