Source: Object.js

'use strict';
if ( ( 0, eval )( 'this' ).XtraUtils && ( 0, eval )( 'this' ).XtraUtils.Utility ) {
	/**
   * @type {Utility}
   * @property {function} aidsIn The Object class
   * @property {Object} aidsIn.prototype the Object prototype
   * @namespace
   * @memberof XtraUtils
   */
	XtraUtils.Object = new XtraUtils.Utility( Object );

	const deepClone = ( a )=>{const types = [Number,String,Boolean];if( !a ){return a;}let b;types.forEach( ( d )=>{a instanceof d && ( b = d( a ) );} );if( 'undefined' === typeof b ){if( '[object Array]' === toString.call( a ) ){b = [],a.forEach( ( a,c,e )=>{b[c] = deepClone( a );} );}else if( 'object' === typeof a ){if( a.nodeType && 'function' == typeof a.cloneNode ){b = a.cloneNode( !0 );}else if( a.prototype ){b = a;}else if( a instanceof Date ){b = new Date( a );}else{b = {};for( var c in a ){a.hasOwnProperty( c ) && ( b[c] = deepClone( a[c] ) );}}}else {b = a;}}setPrototypeOf( b,a.__proto__ );return b;},
		deepEqual = ( c,b ) => {b = b || this;if( b === c ){return!0;}if( b && c && 'object' === typeof b && 'object' === typeof c ){var a = isArray( b ),d = isArray( c );if( a && d ){var e = b.length;if( e !== c.length ){return!1;}for( a = e;0 !== a--; ){if( !deepEqual( b[a],c[a] ) ){return!1;}}return!0;}if( a !== d ){return!1;}a = d instanceof Date;d = c instanceof Date;if( a !== d ){return!1;}if( a && d ){return b.getTime() == c.getTime();}a = isRegExp( b );d = isRegExp( c );if( a !== d ){return!1;}if( a && d ){return b.toString() === c.toString();}d = keys( b );e = d.length;if( e !== keys( c ).length ){return!1;}for( a = e;0 !== a--; ){if( !hasProp.call( c,d[a] ) ){return!1;}}for( a = e;0 !== a--; ){if( e = d[a],!deepEqual( b[e],c[e] ) ){return!1;}}return b.__proto__ !== c.__proto__ ? !1 : !0;}return b !== b && c !== c;},
		forIn = ( obj, fn ) => entries( obj ).forEach( fn ),
		isObject = prop=>prop !== null && typeof prop === 'object',

		{ setPrototypeOf, entries, keys, create, assign } = Object,
		{ reduce } = Array.prototype,
		{ hasOwnProperty, toString } = Object.prototype,
		{ isArray } = Array,

		isDate = obj => obj instanceof Date,
		isRegExp = obj => obj instanceof RegExp;

	XtraUtils.Object.addMethod( 'evolve', ( function(){
		const isFunction = a => typeof a === 'function',
			isNumber = a => typeof a === 'number',
			isObject = a => typeof a === 'object',
			isArray = a => a instanceof Array,
			isBoolean = a => typeof a === 'boolean',
			isString = a => typeof a === 'string';
		/**
		 * Modifies the object based upon `transformation`.
		 * @param {Object} target The target object.
		 * @param {ObjectDiff} transformations The transformations to apply.
		 * @param {boolean} [originalFlag=false] Whether to modify the original object.
		 * @returns {Object} the resulting object.
		 * @memberof XtraUtils.Object.
		 * @example
		 * let obj = {
		 *   x: 1,
		 *   y: 2
		 * };
		 * let transform = {
		 *   x: 1,
		 *   y: String
		 * };
		 * Object.evolve(obj, transform); // { x: 2, y: "3" }
		 */
		function evolve( target, transformations, originalFlag = false ) {
			let self = originalFlag ? target : deepClone( target );
			for( const key in self ) {
			  if( self.hasOwnProperty( key ) ) {
					let value = self[key],
						transform = transformations[key];
					if( transform ) {
						if( isObject( value ) ) {
							if( isFunction( transform ) ) {
								self[key] = evolve( value, transformations[key] );
							} else if( isObject( transform ) ) {
								self[key] = transform;
							}
						} else if( isFunction( transform ) ) {
							self[key] = transform( self[key] );
						} else if( isNumber( value ) ) {
							self[key] += transform;
						} else if( isBoolean( value ) && isBoolean( transform ) ) {
							self[key] = transform;
						}
					}
				}
			}
			return self;
		}
		return evolve;
	} )() );

	XtraUtils.Object.addMethod( 'merge', ( function(){
		/**
		 * Merges 1 or more objects.
		 * @param {Object} target The object to merge onto. Use an empty object to shallow clone and merge.
		 * @param {...Object} sources The objects to merge.
		 * @returns {Object} The merged objects.
		 * @memberof XtraUtils.Object.
		 * @example
		 * // returns { a: 1, b: 2 }
		 * Object.merge({a:1},{b:2})
		 */
		function merge( target, ...sources ) {
			return sources.reduce( ( ret, merger ) => {
				return assign( ret, merger );
			}, target );
		}
		return merge;
	} )() );

	XtraUtils.Object.addMethod( 'mergeDeep', ( function(){
		/**
		 * Merges 1 or more objects on the deep scale.
		 * @param {Object} target The object to merge onto. Use an empty object to deep clone and merge.
		 * @param {...Object} sources The objects to merge.
		 * @returns {Object} The resulting merged objects.
		 * @memberof XtraUtils.Object.
		 * @example
		 * // returns { a: 1, b: 2 }
		 * Object.merge({a:1},{b:2})
		 */
		function mergeDeep( target, ...sources ) {
			sources.forEach( ( source, _ ) => {
				if ( isObject( target ) && isObject( source ) ) {
					for ( const key in source ) {
					  const value = source[key];
						if ( isObject( value ) ) {
							if ( !target[key] ) {
								assign( target, { [key]: {} } );
							}
							mergeDeep( target[key], value );
						} else {
							target[key] = value;
						}
					}
				}
			} );
			return target;
		}
		return mergeDeep;
	} )() );

	XtraUtils.Object.addMethod( 'size', ( function(){
		/**
		 * Finds the number of own keys the object has.
		 * @param {Object} obj The object to get the size of.
		 * @returns {number} The size of the object.
		 * @memberof XtraUtils.Object.
		 * @example
		 * // returns 2
		 * Object.size({a: 1, b: {c: 3}});
		 */
		function size( obj ) {
			let Size = 0;
			for( const key in obj ) {
			  if( hasOwnProperty.call( obj, key ) ) {
			    Size++;
			  }
			}
			return size;
		}
		return size;
	} )() );

	XtraUtils.Object.addMethod( 'sizeDeep', ( function(){
		/**
		 * Finds the number of own keys the object has, and the number of keys it's children has, recursively.
		 * @param {Object} obj The object to get the size of.
		 * @returns {number} The size of the object.
		 * @memberof XtraUtils.Object.
		 * @example
		 * // returns 3
		 * Object.size({a: 1, b: {c: 3}});
		 */
		function sizeDeep( obj ) {
			let size = 0;
			for( const key in obj ) {
				const value = obj[key];
				if( hasOwnProperty.call( obj, key ) ) {
					size++;
					if( isObject( value ) ) {
						size += sizeDeep( value );
					}
				}
			}
			return size;
		}
		return sizeDeep;
	} )() );

	XtraUtils.Object.addMethod( 'clone', ( function(){
		const _clone = obj => setPrototypeOf( assign( {}, obj ),obj.__proto__ );
		/**
		 * Shallow clones the object.
		 * @param {*} obj The item to clone.
		 * @returns {*} The resulting item.
		 * @memberof XtraUtils.Object.
		 */
		function clone( obj ) {
			return _clone( obj );
		}
	} )() );

	XtraUtils.Object.addMethod( 'cloneDeep', ( function(){
		const types = [ Number, String, Boolean ];
		/**
		 * Clones the object on the deep level.
		 * @param {*} obj The item to clone.
		 * @returns {Object} The resulting object
		 * @memberof XtraUtils.Object.
		 * @example
		 * // returns { a: 1, b: 2, c: { d: 4 } }, but the object is not the same
		 * let obj = {
		 *   a: 1,
		 *   b: 2,
		 *   c: {
		 *		 d: 4
		 *   }
		 * }
		 * let clone = obj.deepClone();
		 * clone === obj;		 // false
		 * clone.c === obj.c; // false
		 * clone.a === obj.a; // true
		 */
		function cloneDeep( obj ) {
			if ( !obj ) { return obj; }

			let result;

			types.forEach( type => {
				if ( obj instanceof type ) { result = type( obj ); }
			} ); // normalizing primitives if someone did new String('aaa'), or new Number('444');

			if ( typeof result === 'undefined' ) {
				if ( toString.call( obj ) === '[object Array]' ) {
					result = [];
					obj.forEach( ( child, index, array ) => {
						result[index] = cloneDeep( child );
					} );
				} else if ( typeof obj === 'object' ) {
					if ( obj.nodeType && typeof obj.cloneNode == 'function' ) {
						result = obj.cloneNode( true );
					} else if ( !obj.prototype ) {
						if ( obj instanceof Date ) {
							result = new Date( obj );
						} else {
							result = {};
							for ( const key in obj ) {
							  const value = obj[key];
								if( hasOwnProperty.call( obj, key ) ) {
									result[key] = cloneDeep( value );
								}
							}
						}
					} else {
						if ( false && obj.constructor ) {
							result = new obj.constructor();
						} else {
							result = obj;
						}
					}
				} else {
					result = obj;
				}
			}
			setPrototypeOf( result, obj.__proto__ );
			return result;
		}
		return cloneDeep;
	} )() );

	XtraUtils.Object.addMethod( 'functions', ( function(){
		/**
		 * Gets the keys to all of the functions on an object
		 * @param {Object} obj The object to get the functions from
		 * @returns {Array.<string>} An array of the paths to the functions.
		 * @memberof XtraUtils.Object.
		 * @example
		 * Object.functions({a: 1, b: function(){return 2}}); // ["b"]
		 */
		function functions( obj ) {
			const res = [];
			for( const key in obj ) {
			  const value = obj[key];
			  if( typeof value === 'function' ) {
			    res.push( `.${key}` );
			  }
			}
			return res;
		}
		return functions;
	} )() );

	XtraUtils.Object.addMethod( 'functionsDeep', ( function(){
		const _functionsDeep = ( obj, namePath = '' )=>{
			const res = [];
			for( const key in obj ) {
			  const value = obj[key];
			  if( typeof value === 'function' ) {
			    res.push( `${namePath}.${key}` );
			  } else if ( typeof value === 'object' ) {
			    res.push( ..._functionsDeep( value, `${namePath}.${key}` ) );
			  }
			}
			return ret;
		};
		/**
		 * Gets the keys to all of the functions on an object at the deep level.
		 * @param {Object} obj The object to get the functions from.
		 * @returns {Array.<string>} An array of paths to functions.
		 * @memberof XtraUtils.Object.
		 * @example
		 * // returns ['b']
		 * Object.functions({a: 1, b: function(){return 2}});
		 */
		function functionsDeep( obj ) {
			return _functionsDeep( obj );
		}
		return functionsDeep;
	} )() );

	XtraUtils.Object.addMethod( 'fromPairs', ( function(){
		/**
		 * Creates an object from an array of key-value pairs.
		 * @param {Array.<Array>} pairs The key-value pairs to create the object from.
		 * @returns {Object} The resulting new object.
		 * @memberof XtraUtils.Object.
		 * @example
		 * // returns { a: 1, b: 2 }
		 * Object.fromPairs([[a,1],[b,2])
		 */
		function fromPairs( pairs ) {
			const length = pairs == null ? 0 : pairs.length;
			let i = length,
				result = {};
			while( i-- ) {
				let pair = pairs[i];
				result[pair[0]] = pair[1];
			}
			return result;
		}
		return fromPairs;
	} )() );

	XtraUtils.Object.addUtil( 'pick', ( function(){
		/**
		 * Selects properties from an object.
		 * @param {...string} pickedKeys The keys to pick
		 * @returns {Object} The resulting object
		 * @memberof XtraUtils.Object#
		 * @example
		 * // returns { a: 1, __omitted__: Object }
		 * let obj = {
		 *   a: 1,
		 *   b: 2
		 * };
		 * obj.pick("a");
		 */
		function pick( ...pickedKeys ) {
			this.__omitted__ = assign( {}, this.__omitted__ );
			for( const key in this ) {
			  const value = this[key];
			  if( !pickedKeys.includes( key ) && key !== '__omitted__' ) {
			    this.__omitted__[key] = value;
			    delete this[key];
			  }
			}
			return this;
		}
		return pick;
	} )() );

	XtraUtils.Object.addUtil( 'omit', ( function(){
		/**
		 * Removes properties from the visible object.
		 * @param {...string} omittedKeys The keys to omit
		 * @returns {Object} The resulting object
		 * @memberof XtraUtils.Object#
		 * @example
		 * // returns { b: 2, __omitted__: Object }
		 * let obj = {
		 *   a: 1,
		 *   b: 2
		 * };
		 * obj.omit("a");
		 */
		function ommit( ...omittedKeys ) {
			this.__omitted__ = assign( {}, this.__omitted__ );
			omittedKeys.forEach( ( key ) => {
				this.__omitted__[key] = this[key];
				delete this[key];
			} );
			if( deepEqual( this.__omitted__, {} ) ) {
				delete this.__omitted__;
			}
		}
		return ommit;
	} )() );

	XtraUtils.Object.addUtil( 'show', ( function(){
		/**
		 * Shows all hidden properties, or only properties selected.
		 * @param {...string} shownKeys The keys to omit, or, if empty, shows all keys.
		 * @returns {Object} The resulting object
		 * @memberof XtraUtils.Object#
		 * @example
		 * // returns { a:1, b: 2 }
		 * let obj = {
		 *   a: 1
		 *   __omitted__: {
		 *		b: 2
		 *  }
		 * };
		 * obj.show();
		 * @desc Which is the same as:
		 * let obj = {
		 *   a: 1
		 *   __omitted__: {
		 *		b: 2
		 *  }
		 * };
		 * obj.show("b");
		 */
		function show( ...shownKeys ) {
			if( shownKeys.length === 0 ){ shownKeys = keys( this.__omitted__ );}
			shownKeys.forEach( ( key ) => {
				this[key] = this.__omitted__[key];
				delete this.__omitted__[key];
			} );
			if( deepEqual( this.__omitted__, {} ) ) {
				delete this.__omitted__;
			}
		}
		return show;
	} )() );

	XtraUtils.Object.addMethod( 'deepGet', ( function(){
		const regularizePath = ( path ) => {
			if( path instanceof Array ) {
				return path;
			} else if( typeof path === 'string' ) {
				let res = path.replace( /(?:\[(\"|\')([^[\]]+)\1\])/, '.$2' );
				while( res != path ){
					path = res;
					res = res.replace( /(?:\[(\"|\')([^[\]]+)\1\])/, '.$2' );
				}
				res = res.split( /\./g );
				if( res[0] === '' ) { res.shift();}
				return res;
			}
		};
		/**
		 * Gets a property far down the property chain.
		 * @param {Object} obj The object to get the deep property from.
		 * @param {Array.<string>|string} path The path to the property.
		 * @returns {*} The property
		 * @memberof XtraUtils.Object#
		 * @example
		 * // returns 2
		 * let obj = {
		 *   a: 1,
		 *   others: {
		 *		 b: 2
		 *   }
		 * };
		 * obj.deepGet(['others','b']);
		 * @example
		 * // returns 2
		 * let obj = {
		 *   a: 1,
		 *   others: {
		 *		 b: 2
		 *   }
		 * };
		 * obj.deepGet('.others.b');
		 */
		function deepGet( obj, path ) {
			path = regularizePath( path );
			let length = path.length;
			for ( var i = 0; i < length; i++ ) {
				if ( obj == null ) {return void 0;}
				obj = obj[path[i]];
			}
			return length ? obj : void 0;
		}
		return deepGet;
	} )() );

	XtraUtils.Object.addUtil( 'isEmpty', ( function(){
		/**
		 * Tells if an object is empty.
		 * @returns {boolean} Whether the object is empty or not
		 * @memberof XtraUtils.Object#
		 * @example
		 * // returns false
		 * let obj = {
		 *   a: 1
		 * };
		 * obj.isEmpty();
		 * @example
		 * // returns true
		 * let obj = {};
		 * obj.isEmpty();
		 */
		function isEmpty(){
			return keys( this ).length === 0;
		}
		return isEmpty;
	} )() );

	XtraUtils.Object.addUtil( 'conforms', ( function(){
		/**
		 * Tells if an object matches the criteria of a function provided. In this method we leave most to the user in the function passed.
		 * @param {Function} fn The function to test on the object
		 * @param {boolean} [originalFlag=false] Whether to give the function the original object
		 * @returns {boolean} Whether the object matches criteria
		 * @memberof XtraUtils.Object#
		 * @example
		 * // returns false
		 * let obj = {
		 *   a: 1
		 * };
		 * obj.conforms(function(obj){return obj.a == 2});
		 * @example
		 * // returns true
		 * let obj = {
		 *   b: 2
		 * };
		 * obj.conforms(function(obj) { return obj.b == 2});
		 */
		function conforms( fn, originalFlag = false ) {
			return Boolean( fn( originalFlat ? this : deepClone( this ) ) );
		}
		return conforms;
	} )() );

	XtraUtils.Object.addUtil( 'invert', ( function(){
		const fromPairs = ( a )=>{for( var b = null == a ? 0 : a.length,c = {};b--; ){var d = a[b];c[d[0]] = d[1];}return c;};
		/**
		 * Inverts the object's keys and values.
		 * @param {boolean} [originalFlag=false] Whether to invert the original object or return a new object
		 * @returns {Object} The resulting object
		 * @memberof XtraUtils.Object#
		 * @example
		 * // returns { 1: 'a', 2: 'b' }
		 * let obj = {
		 *   a: 1,
		 *   b: 2
		 * };
		 * obj.invert();
		 */
		function invert( originalFlag = false ){
			return assign( originalFlag ? this : {}, fromPairs( entries( this ).map( item => item.reverse() ) ) );
		}
		return invert;
	} )() );

	XtraUtils.Object.addUtil( 'getKeysFor', ( function() {
		const _invert = obj => entries( obj ).map( item => item.reverse() ),
			_isPrimitive = obj => typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean';
		/**
		 * Gets the possible keys for a value.
		 * @param {*} property The value.
		 * @returns {string[]} An array of possible keys.
		 * @memberof XtraUtils.Object#
		 * @example
		 * // returns [ "a" ]
		 * let obj = {
		 *   a: 1,
		 *   b: 2
		 * }
		 * obj.getKeysFor(1);
		 */
		function getKeysFor( property ) {
		  const res = [];
			for( const key in this ) {
			  const value = this[key];
			  if( _isPrimitive( value ) && _isPrimitive( property ) ) {
			    if( value === property ) {
			      res.push( key );
			    }
			  } else {
			    if( deepEqual( value, property ) ) {
			      res.push( key );
			    }
			  }
			}
			return res;
		}
		return getKeysFor;
	} )() );

	XtraUtils.Object.addMethod( 'truthMap', ( function(){
		const assignTrue = ( a,b )=>{a[b[0]] = b[1];return a;};
		/**
		 * Assigns true to a value based on it's truthiness.
		 * @param {Object} obj The object to test the truthiness of.
		 * @param {Callback} [callBack=(..._)=>{return false}] The function to test the truthiness of a property.
		 * @param {boolean} [originalFlag=false] Whether to modify the original object or return a new object.
		 * @param {...*} [args] The arguments to hand to the function.
		 * @returns {Object} The object, with properties reduced to truthiness.
		 * @memberof XtraUtils.Object.
		 * @example
		 * // returns { a: true, b: true, c: false }
		 * let obj = {
		 *   a: 1,
		 *   b: 2,
		 *   c: 0
		 * };
		 * Object.truthMap(obj, function(key, value) {
		 *   return Boolean(value);
		 * });
		 */
		function truthMap( obj, callBack = ( ..._ )=>{return false;}, originalFlag = false, ...args ) {
			let self = originalFlag ? obj : deepClone( obj );
			return assign( self, enries( self ).map( ( [key, value], index ) => {
				return [key, callBack( key, value, self, ...args )];
			} ).reduce( assignTrue, {} ) );
		}
		return truthMap;
	} )() );

	XtraUtils.Object.addUtil( 'objReduce', ( function(){
		return function objReduce( callBack = ( ..._ )=>{return false;}, originalFlag = false, ...args ) {
			let self = originalFlag ? this : deepClone( this );
			entries( self ).reduce( ( ret, [key, value], _ ) => {
				return callBack( key, value, self, ...args );
			}, create( null ) );
		};
	} )() );

	XtraUtils.Object.addUtil( 'some', ( function(){
		/**
		 * Tests to see if some properties pass a test.
		 * @param {Callback} [callBack=(..._)=>{return false}] The function to test the object with.
		 * @param {boolean} [originalFlag=false] Whether to hand the function the original object or a duplicate.
		 * @param {...*} [args] The arguments to hand to the function.
		 * @returns {boolean} Whether some of the properties pass the function.
		 * @memberof XtraUtils.Object#
		 */
		function some( callBack = ( ..._ )=>{return false;}, originalFlag = false, ...args ) {
			let self = originalFlag ? this : deepClone( this ),
				ownKeys = keys( this ),
				i = ownKeys.length + 1;
		  for( const key in self ) {
		    const value = self[key];
		    if( callBack( key, value, self, ...args ) ) {
		      return true;
		    }
		  }
			return false;
		}
		return some;
	} )() );

	XtraUtils.Object.addUtil( 'iterate', ( function(){
		/**
		 * Iterates over each property in an object.
		 * @param {CallBack} [callBack=(..._)=>{return false}] The function to call for each property. Although this is optional, this is solely for the purpose of consistency. The default callBack will not function.
		 * @param {boolean} [originalFlag=false] Whether to give it the original object;
		 * @param {...*} [args] The arguments to hand the function.
		 * @returns {undefined}
		 * @memberof XtraUtils.Object#
		 */
		function iterate( callBack = ( ..._ )=>{return false;}, originalFlag = false, ...args ) {
			let self = originalFlag ? this : deepClone( this );
			for( const key in self ) {
			  const value = self[key];
			  callBack( key, value, self, ...args );
			}
		}
		return iterate;
	} )() );

	XtraUtils.Object.addMethod( 'deepEqual', ( function(){
		const isDate = obj => obj instanceof Date,
			isRegExp = obj => obj instanceof RegExp;
		/**
		 * Tests if the properties of an object are equal, recursively.
		 * @param {*} obj1 The first object to compare
		 * @param {*} obj2 The second object to compate
		 * @returns {boolean} Whether the objects are equal
		 * @memberof XtraUtils.Object#
		 */
		function deepEqual( obj1, obj2 ) {
			const a = obj1;
			b = obj2;
			if ( a === b ) {
				return true;
			} else if ( a && b && typeof a === 'object' && typeof b === 'object' ) {
				const arrA = isArray( a ),
					arrB = isArray( b );
				let i, length, key;

				if ( arrA && arrB ) {
					length = a.length;
					if ( length !== b.length ) { return false; }
					for ( i = length; i-- !== 0; ) {
						if ( !deepEqual( a[i], b[i] ) ) {
							return false;
						}
					}
					return true;
				}

				if ( arrA !== arrB ) {
					return false;
				}

				const dateA = isDate( a ),
					dateB = isDate( b );

				if( dateA !== dateB ) {
					return false;
				}
				if ( dateA && dateB ) {
					return a.getTime() == b.getTime();
				}

				const regexpA = isRegExp( a ),
					regexpB = isRegExp( b );

				if ( regexpA !== regexpB ) {
					return false;
				}
				if ( regexpA && regexpB ) {
					return a.toString() === b.toString();
				}

				let keys1 = keys( a );
				length = keys1.length;

				if ( length !== keys( b ).length ) {
					return false;
				}
				for ( i = length; i-- !== 0; ) {
					if ( !hasOwnProperty.call( b, keys1[i] ) ) {
						return false;
					}
				}
				for ( i = length; i-- !== 0; ) {
					key = keys1[i];
					if ( !deepEqual( a[key], b[key] ) ) {
						return false;
					}
				}
				if( a.__proto__ !== b.__proto__ ) {
					return false;
				}

				return true;
			}

			return a !== a && b !== b;
		}
		return deepEqual;
	} )() );

} else {
	console.warn( 'XtraUtils is not defined. For more details, please visit ' +
						   'https://github.com/FreezePhoenix/XtraUtils/. If your issue still occurs, submit an issue here:' +
						   'https://github.com/FreezePhoenix/XtraUtils/issues/new' );
}
/**
 * @alias ObjectDiff
 * @typedef {Object.<string,(EvolveCallBack|ObjectDiff)>} ObjectDiff
 * @memberof XtraUtils.Object
 */
/**
 * The callback class for `Object.evolve`.
 * @alias EvolveCallBack
 * @callback EvolveCallBack
 * @param {*} [value] The old value.
 * @returns {*} The new value.
 * @memberof XtraUtils.Object
 */
/**
 * The base callback class. All callbacks have a default for consistency. In the case of `Object.prototype.reduce`, the function is handed the merger as the fourth argument, and remainder of arguments are post-pended to the end if that.
 * @alias CallBack
 * @callback CallBack
 * @param {string} [key] An object key.
 * @param {*} [value] The value of the item.
 * @param {Object} [obj] The object being called from
 * @param {...*} [args] Static arguments determined at call
 * @returns {*}
 * @default (..._)=>{return false}
 * @memberof XtraUtils.Object
 */