Last article case | native handwriting a round – fade fade version there are a lot of deficiencies, here to thank the big man CSdoker give valuable advice and guidance 🙏, so the author decided to perfect the round map case, intends to do a simple version of the left and right round map plug-in package;

Think of the original intention of the author to write the article is to summarize the knowledge and strive to be easy to understand, so today the author intends to lay down several important knowledge points that need to be used: deep clone VS shallow clone, deep comparison VS shallow comparison, basic knowledge of callback function;

First, deep clone VS shallow clone

Mind mapping

1. Shallow clone

-1) Definition:

Just copy the first level and assign it to the new array (generally we implement array cloning is shallow clone)

-2) Methods:

  • Slice:
    • Clone principle: create a new array, loop each item in the original array, assign each item to the new array
    • let arr2 = arr1.slice(0);
  • Concat:
    • let arr2 = arr1.concat();
  • Extension operator […ary] :
    • let arr2 = […arr1];
  • . Etc.

2, deep cloning

-1) Definition:

Not only do we clone the first level to the new array, but if there are multiple levels in the original array, we clone each level and assign values to each level of the new array

-2) Method 1: Use JSON data format

  • Grammar:
    • let arr2 = JSON.parse(JSON.stringify(arr1));
  • Implementation principle:
    • Json.stringify (arr1) : First turn the original object into a string (remove the heap-heap-nested relationship)
    • JSON.parse(…) After converting the string to a new object, the browser reopens memory to store the information
  • Application:
    • rightdigital/string/Boolean/null/Ordinary objects/The array objectEtc have no impact, can be used
  • Disadvantages:
    • Json.stringify (arR1) : Not all values are handled efficiently

      • The re becomes an empty object
      • The function /undefined/Symbol will become NULL
      • In this way, the cloned information differs from the original data
    • After date format data is changed to a string, object format is not returned based on Parse

  • Take 🌰 : a perverted array
let arr1 = [10, '20', [30, 40], /\d+/, function () {}, null, undefined, {
    xxx: 'xxx'
}, Symbol('xxx'), new Date()];
Copy the code

-3) Method two: Self encapsulation

  • Grammar:
    • let arr2 = cloneDeep(arr1);

Ideas:

  • 1. When a function is passed in, no operation is required, just return it

    • Since there can only be one function with one name in an execution environment stack, if we clone another function ourselves, we will replace the original one, which makes no sense
  • 2. When the basic data type is passed in, no operation is required

  • 3. When an object type is passed

    • (1). Re object: Create a new instance to store the current re (because our purpose is to make the space address different)
    • (2). Date object: Create a date instance to store the current date
    • (3). Common object && array object: create a new instance, cyclic store current information;
      • There may also be multiple nested relationships in the ordinary && array object, so we can use lower recursion here
  • Code implementation:
function _cloneDeep(obj) {
	// If the Symbol is not an object, it returns the original value.
	if (obj === null) return null;
	if (typeofobj ! = ="object") return obj;

	// Filter out special objects (regular objects or date objects) : create a new instance of the current class using the original value, so that the cloned instance is the new one, but with the same value as before
	if (obj instanceof RegExp) return new RegExp(obj);
	if (obj instanceof Date) return new Date(obj);

	// If we pass an array or object, we need to create a new array or object to store the original data
	// obj.constructor gets the current value (Array/Object)
	let cloneObj = new obj.constructor;
	for (let key in obj) {
		// Loop over each item in the original data, assigning each item to the new object
		if(! obj.hasOwnProperty(key))break;
		cloneObj[key] = _cloneDeep(obj[key]);
	}
	return cloneObj;
}
Copy the code

Second, deep comparison VS shallow comparison

First of all, what does that mean?

Take the subject:

let obj1 = {
    name: 'Little Sesame'.age: 10.teacher: {
        0: 'Joe'.1: 'bill'}};let obj2 = {
    age: 20.school: "Beijing".teacher: {2: "Fifty"}};Copy the code

When we want to merge the above two objects, we get into the problem of “comparison” (I’m not sure why it is called “comparison”);

  • Both objects have age, school, teacher attributes; In which we seeteacherThe value of is an object, and the content is not the same, so when merge, what will be the result?

So that’s what we’re going to talk about in depth comparison;

1. Shallow comparison

-1) Definition:

Combine two objects into one

-2) Methods:Object.assign(obj1,obj2)

  • Merge two objects (replace the previous one with the latter) and return the merged new object
  • The merge in this method is shallow comparison: only the first level is compared

Or this

let obj1 = {
    name: 'Little Sesame'.age: 10.teacher: {
        0: 'Joe'.1: 'bill'}};let obj2 = {
    age: 20.school: "Beijing".teacher: {2: "Fifty"}};let obj = Object.assign(obj1,obj2);
console.log(obj);
Copy the code

The output is 👇

Among them, the simultaneously shared attribute teacher is an object data type. After only comparing one level, the spatial address of teacher value of the previous item (obj1) is replaced with the spatial address corresponding to the latter item (obj2).

Many times we want the effect is not so, we want to be the same attribute name corresponding to the attribute value is also merged, as in the above question teacher attribute merged should be {0: ‘zhang SAN ‘, 1:’ Li Si ‘, 2: “Wang wu “}, this time we need to carry out a deep comparison

2. Deep comparison

  • Grammar:
    • let res = _assignDeep(obj1,obj2)

Ideas:

  • 1. First, clone obj1

  • 2. Loop through each item of Obj2 and compare it to the cloned obj1.

    • If this is an object datatype and the same property name in the clone obj1 corresponds to the object datatype value,
      • Let’s do the deep comparison again, just do it recursively;
    • In all other cases, replace the value of obj1 with the value of obj2;
  • Code implementation:
function _assignDeep(obj1, obj2) {
    // Assign a deep clone of each item in OBJ1 to the new object
    let obj = _cloneDeep(obj1);

    // Replace each item in OBJ with OBJ2
    for (let key in obj2) {
        if(! obj2.hasOwnProperty(key))break;
        let v2 = obj2[key],
            v1 = obj[key];
        // If OBJ2 is traversed by an object and OBJ is also an object, it cannot be replaced directly, and the two objects need to be merged again, and the latest result of the merger is assigned to the item in the new object
        if (typeof v1 === "object" && typeof v2 === "object") {
            obj[key] = _assignDeep(v1, v2);
            continue;
        }
        obj[key] = v2;
    }
    return obj;
}
Copy the code

Callback function

The conventional callback function parameter name is callback

Mind mapping

1. Definition:

Pass a function as a value to another function, and execute that function in another function

2, the characteristics of

During the execution of a large function, we can pass “free” operations to its callback function

  • 1. It can be executed (zero to multiple times)
  • 2. You can also pass arguments to callback functions
  • 3. You can also change this
    • Be careful if the callback is an arrow function
    • Arrow functions do not have THIS; they use THIS in context
  • 4. You can also accept the return result of the function execution
function func(callback) {
	// callback => anonymous
	// We can manipulate the FUNC callback as much as we like during the FUNC function execution
	// 1. It can be executed (zero to multiple times)
	// 2. You can also pass arguments to the callback function
	// 3. You can also change THIS
	// 4. You can also accept the return result of the function execution
	for (let i = 0; i < 5; i++) {
		// callback(i); //=> Pass the value of I in each loop as an argument to Anonymous, so anonymous is executed 5 times in total. Each execution can obtain the value of I passed based on the parameter index
		let res = callback.call(document, i);
		// res is the result returned by each anonymous execution
		if (res === false) {
			// Accept the result of the callback function, the end of the control loop
			break;
		}
	}
}

func(function anonymous(index) {
	// console.log(index, this);
	if (index >= 3) {
		return false;
	}
	return The '@' + index;
});

func((index) = > {
 	// Arrow function does not have THIS, use THIS is in context
 	console.log(index, this);
}); 
Copy the code

The classic use of several callback functions

There are a lot of arguments that are callback functions

  • 1, array iteration method forEach
    • arr.forEach(item=>{})
    • When forEach executes, it iterates through each item in the array, and each iterating through the item executes the arrow function we passed in once
  • 2. Ajax in JQ
    • $. Ajax ({url: “‘ success: the function () {/ / request is successful to transfer function performs}});
  • 3. Event binding
    • window.addEventListener(‘scroll’,function(){});
  • . Etc.

4, Encapsulate an iterated method (for: array/array-like/object)

  • Definition: a powerful iterator

  • _each([ARRAY/OBJECT/ class ARRAY],[CALLBACK])

  • @ params:

    • Obj: Array, class array, object to iterate over
    • Callback: The callback function that is triggered to execute each iteration
      • Parameter: item: current item
      • Parameter: index: indicates the index of the current item
    • Context: THIS of the callback function to be changed
  • @return: Returns a new array/object after processing

  • Function:

    • 1, You can iterate over groups, class arrays, and objects, and execute [CALLBACK] every time you iterate
    • 2. Each time the callback is executed, the result of the current iteration (current item/index) is passed to the callback
    • 3. A third argument is supported to change the execution of THIS in the callback (not passed, WINDOW by default).
    • 4, support callback function return value, each return value will be the current collection of the value of this item replaced; If the callback returns FALSE (which it must), the loop ends
  • Code implementation

// Check whether it is an array or a class array
function isArrayLike(obj) {
    letlength = !! obj && ("length" in obj) && obj.length;
    return Array.isArray(obj) || length === 0| | -typeof length === "number" && length > 0 && (length - 1) in obj);
}

function _each(obj, callback, context = window) {
    //=> Make a deep clone of the original data. The later operations are the results of the clone, and the original data will not be changed
    obj = _cloneDeep(obj); 

    // Verify parameter validity
    if (obj == null) {
        //=>null undefined  
        / / manual throws an exception information, once thrown, console complains, the following code is not executed the Error/TypeError/ReferenceError/SyntaxError...
        throw new TypeError('OBJ must be an object/array/class array! ');
    }
    if (typeofobj ! = ="object") {
        throw new TypeError('OBJ must be an object/array/class array! ');
    }
    if (typeofcallback ! = ="function") {
        throw new TypeError('CALLBACK must be a function! ');
    }

    // Start loop (array and class array based on FOR loop, object loop based on FOR IN)
    if (isArrayLike(obj)) {
        // Array or class array
        for (let i = 0; i < obj.length; i++) {
            // Each iteration executes the callback function, passing the argument that the current iteration and the corresponding index are iterated
            // And change its THIS
            // RES is the return value of the callback function
            let res = callback.call(context, obj[i], i);
            if (res === false) {
                // Return FALSE to end the loop
                break;
            }
            if(res ! = =undefined) {
                // If there is a return value, replace this item in the current arrayobj[i] = res; }}}else {
        / / object
        for (let key in obj) {
            if(! obj.hasOwnProperty(key))break;
            let res = callback.call(context, obj[key], key);
            if (res === false) break;
            if(res ! = =undefined) obj[key] = res; }}return obj;
}
Copy the code

All right, in the basics we’re going to lay out these;

The next main do: left and right round broadcast graph plug-in encapsulation (just a simple thinking and imitation)

I know that the road ahead is still very long, plug-in packaging is an important topic, although the ability is limited, but to dare to try, I hope to grow with you under the supervision of 😄