This is the 25th day of my participation in the August Genwen Challenge.More challenges in August

preface

Filter calls the callback function once for each element in the array and creates a new array with all the elements that cause callback to return true. Callback will only be called on indexes that have been assigned, not indexes that have been deleted or never assigned. Elements that do not pass the callback test are skipped and not included in the new array, which is usually used to filter elements in the array that meet our expected criteria.

var newArray = arr.filter(callback(element[, index[, array]])[, thisArg])
Copy the code

Its function signature is as follows:

  • Callback: A callback function that tests whether each element meets a condition, returning true if it does, and false otherwise.

    • Element: The element currently being processed.
    • Index: Index of the element being processed
    • Array: indicates the array itself.
  • ThisArg: this as callback call.

Filter does not alter the original array, but returns the filtered new array.

Also, the scope of the iterated elements is determined before the first callback is called. Elements added to the array after a call to filter are not traversed by filter. If existing elements are changed, the value they pass to the callback is the value filter traverses up to their moment, and null values are ignored.

ECMA specification

ECMAScript® 2022 Language Specification (TC39.es) – Array.prototype.filter

The general meaning is as follows:

  1. willthisVisualize as an object variable for subsequent operations.
  2. Initializes the arraylengthThat’s rightlengthValue to be legitimized, it must be an integer in a valid range.
  3. Determine whether a callback function existscallbackIf not, throw oneTypeErrorThe exception.
  4. Create an array of the same length as the array to store the result of the callback and as the return value.
  5. Iterate over each element, process it one by one using the callback function, and determine the value returned by the callback function iftrueIs added to the result array, otherwise the next iteration is performed.
  6. When all elements are processed, the result array is returned.

Handwritten implementation

According to the above analysis, to handwritten implementation

Array.prototype._filter = function(callback, thisArg) {
  // First check whether the callback function is valid and whether the current environment's this has a value to point to.
  if ( !(typeof callback === 'function' && this))throw new TypeError(a);// Initializes the length value, which must be a representable integer in the range
  let length = this.length >>> 0,
      result = new Array(length),
      o = Object(this),len = 0, i = -1;
  ThisArg does not exist
  if (thisArg === undefined) {while(++i ! == length){if (i in this) {ThisArg does not need to be bound when calling the callback function
        if(callback(o[i], i, o)){ result[len++] = o[i]; }}}}/ / thisArg exist
  else{
    while(++i ! == length){if (i in this) {ThisArg is bound when the callback is called
        if (callback.call(thisArg, o[i], i, o)){
          result[len++] = o[i];
        }
      }
    }
  }
  result.length = len;
  return result;
};
Copy the code

It is worth noting that, unlike the Map method, the Filter does not maintain the same sparse structure as the calling array, although both methods return a processed array.

[,,1.2.3.4.filter(i= >true)/ / [1, 2, 3, 6]
Copy the code

Although the filter condition is true, the new array ignores the null value of the original array, which is noteworthy in our handwritten implementation, where len represents the current length of the result array, and the empty element is ignored instead of being processed by the callback function, so len increment is not performed.

V8 implementation

V8 / array.js-filter

// The following functions cannot be made efficient on sparse arrays while
// preserving the semantics, since the calls to the receiver function can add
// or delete elements from the array.
// The core of the filter method
function InnerArrayFilter(f, receiver, array, length, result) {
  var result_length = 0;
  for (var i = 0; i < length; i++) {
    if (i in array) {
      var element = array[i];
      if(%_Call(f, receiver, element, i, array)) { %CreateDataProperty(result, result_length, element); result_length++; }}}return result;
}



function ArrayFilter(f, receiver) {
   The CHECK_OBJECT_COERCIBLE function checks this to see if it can be reified as an object variable, and throws an exception if it can't
  CHECK_OBJECT_COERCIBLE(this."Array.prototype.filter");

  // Pull out the length so that modifications to the length in the
  // loop will not affect the looping and side effects are visible.
  
  // This is visualized as an object variable, which is the array to which this points
  var array = TO_OBJECT(this);
  // length is valid
  var length = TO_LENGTH(array.length);
  // Check whether the callback function is valid
  if(! IS_CALLABLE(f))throw %make_type_error(kCalledNonCallable, f);
  // Create a result array of the same length
  var result = ArraySpeciesCreate(array, 0);
  // The InnerArrayFilter is responsible for returning the processed array data and the processed array
  return InnerArrayFilter(f, receiver, array, length, result);
}
Copy the code

Comparing our implementation with v8’s, our implementation is similar to V8’s, except that V8 encapsulates some security judgments and boundary handling, and uses the language’s most basic expressions and self-encapsulated functions in the implementation, so there are no compatibility issues.

According to the ECMA specification, the filter method is designed to be generic, not just for arrays, so it can be reused for arrays like objects or objects with array-related properties.