JS callback function

We pass a function as a value to another function, and execute the function in another function. This function is called a callback function. In JS we have a convention to write the callback function parameter as callback

  • The callback function we use all the time
    • Iterating methods in arrays (forEach, replace…)
    • Timers (setTimeout, setInterval)
    • Success function in AJAX request in JQ
    • DOM2 event (addEventListenter)
function func(callback{

 // callback => anonymous

 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{

 if (index >= 3) {

  return false;

 }

 return The '@' + index;

});

Copy the code

During the func function execution, we can manipulate the callback function “as much as we like”

  • It can be executed zero to multiple times
  • You can also pass arguments to the callback function
  • You can also change the “THIS” inside
  • You can also accept the return of the function execution

To make better use of the callback function, we use the callback function to wrap a powerful _each method

  • Ideas:
    • _EACH([VALUE],[CALLBACK],[CONTEXT]), we can pass three parameters
    • You can iterate through arrays of numbers, class arrays, and objects, and execute [CALLBACK] on each iteration
    • Each time the callback is executed, the result of the current iteration (current item \ index) is passed to the callback
    • A third argument is supported to change the reference to THIS in the callback (not passed, default is WINDOW).
    • Support callback function return value, each return value will be the current collection of the value of the value of the replacement; If the callback returns FALSE (which it must), the loop ends
// 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 - 1in obj);

}



function _each(obj, callback, context = window{

 obj = _cloneDeep(obj); //=> 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



 // 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 array

    obj[i] = res;

   }

  }

 } else {

  / / object

  for (let key in obj) {

   if(! obj.hasOwnProperty(key))break;

   let res = callback.call(context, obj[key], key);

   if (res === falsebreak;

   if(res ! = =undefined) obj[key] = res;

  }

 }

 return obj;

}

Copy the code

Wrapping _each also uses the _cloneDeep function, which we also wrapped ourselves

Deep cloning, shallow cloning is often asked in our interview, here we also have a further understanding

  • Shallow clone: only the first level of a copy assigned to the new array, we generally implement array cloning is shallow clone
  • Deep clone: not only does the first level clone a copy of the new array, but if there are multiple levels in the original array, then each level clone is assigned to each level of the new array
  • The method of realizing shallow clone
let arr = [10.20[30.40]].

let arr1 = arr.slice(0);// Assign an assignment to the new array arr1 using the slice method in the array

let arr2 = arr.concat();// Concatenate an array with the concat method, which is also equivalent to copying an array

let arr3 = [...arr];// Assign a copy of arR to the new array with the remainder operator

Copy the code
  • Implement deep cloning
// json.stringify (arr) turns the original object into a string (removing the heap-heap-nested relationship)

//JSON.parse(...) After converting the string to a new object, the browser reopens memory to store the information

let arr4 = JSON.parse(JSON.stringify(arr));

Copy the code

But json.stringify does not handle all values efficiently:

  • The re becomes empty
  • The function /undefined/Symbol is null
  • Date format data will no longer be in date object format based on PARSE after it becomes a string
  • But it doesn’t affect numbers/strings/booleans/NULL/normal objects/array objects, etc
  • In this way, the cloned information differs from the original data
  • So we encapsulated a method that could be used for deep cloning
function _cloneDeep(obj{

 // If the Symbol is not an object, it returns the original value.

 if (obj === nullreturn 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 RegExpreturn new RegExp(obj);

 if (obj instanceof Datereturn 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

This article is formatted using MDNICE