preface

The learning underscore. Js source code version is 1.13.1

Rest Parameter (remaining parameters)

The ES6 residual argument syntax allows us to represent an indefinite number of arguments as an array.

function fn(a, b, ... args) {
  console.log(args); / / [3, 4, 5]
}

fn(1.2.3.4.5);


function sum(. theArgs) {
  return theArgs.reduce((previous, current) = > {
    return previous + current;
  });
}

console.log(sum(1.2.3));  / / 6

console.log(sum(1.2.3.4));  / / 10
Copy the code

The differences between the remaining arguments and arguments objects

  • The remaining arguments contain only arguments that have no corresponding parameters, whereas the Arguments object contains all arguments passed to the function.
  • The Arguments object is not a true array (nothing except the length attribute and index elementArrayProperty), and the remaining parameters are real Array instances, which means you can use all Array methods directly on it, for examplesort.map.forEachorpop.
  • Arguments objects also have additional properties (such as the Callee property)

How is an array of classes converted to an array?

Class array: An object with a Length attribute and several attributes. Underscore (isArrayLike)

var MAX_ARRAY_INDEX = Math.pow(2.53) - 1;

var isArrayLike = function(obj) {
  var sizeProperty = obj == null ? void 0 : obj.length
  return typeof sizeProperty == 'number' && sizeProperty >= 0 && sizeProperty <= MAX_ARRAY_INDEX;
}
Copy the code

That is, an object with a length attribute whose value is not greater than number. MAX_SAFE_INTEGER is considered a class array

Common array-like objects include:

  1. Arguments object in function
  2. The use of the document. The getElementsByTagName/ClassName () method so as to obtain the HTMLCollection;
  3. NodeList obtained using querySelector
  4. .

How to convert?

1. ES6 expansion operator (applicable only to Iterable objects) A data structure that is said to be “iterable” whenever the Iterator interface is deployed. The default Iterator interface is deployed in the symbol. Iterator property of a data structure, or a data structure can be considered “iterable” as long as it has the symbol. Iterator property. The symbol. iterator property is itself a function that is the default traverser generator for the current data structure.

The data structures with the native Iterator interface are as follows.

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • The arguments object for the function
  • The NodeList object

So using the expansion operator for data that does not have an Iterator interface is an error. You can learn about Iterator from Ruan Yifeng es6.ruanyifeng.com/#docs/itera…

function sum(a, b){
  var args = [...arguments];
  args.push(3);
  console.log(args.reduce((prev, next) = > prev + next ,0));
}
sum(1.2);  / / 6

[...undefined];   // Uncaught TypeError: undefined is not iterable{[...length: 3}]; // Uncaught TypeError: {(intermediate value)} is not iterable
Copy the code

2. Using the Array. The from ()

function sum(a, b){
  var args = Array.from(arguments);
  args.push(3);
  console.log(args.reduce((prev, next) = > prev + next ,0));
}
sum(1.2);
Copy the code

3. In ES5, you can use the Array API to call/apply and change this or arguments to convert the following Array of classes:

var arrayLike = {
  0: 3.1: 4.2: 5.length: 3
}
Copy the code
Array.apply(null, arrayLike);  / / use the arguments
Array.prototype.concat.apply([], arrayLike)  / / use the arguments
Array.prototype.slice.call(arrayLike)  / / use this
Array.prototype.map.call(arrayLike, x= > x)  / / use this
Copy the code

Using array (n) creates a sparse array. To save space, the sparse array contains non-real elements that will be displayed as empty on the console, as shown below:

console.log([,,,]);  / / / the empty x 3
console.log(Array(3));  / / / the empty x 3
Copy the code

When the class array is {length: 3}, any method that calls the class array as this will return a sparse array, and any method that calls the class array as arguments will return a dense array:

var arrayLike1 = {
  length: 3
}
Array.apply(null, arrayLike1);  // [undefined, undefined, undefined]
Array.prototype.concat.apply([], arrayLike1)  // [undefined, undefined, undefined]
Array.prototype.slice.call(arrayLike1)  / / / the empty x 3
Array.prototype.map.call(arrayLike, x= > x)  / / / the empty x 3
Copy the code

restArguments

If not used… How to implement the extension operator, using only ES5 content?

We could write a restArguments function, pass in a function, and store the rest of the function’s parameters with the last parameter of the new function, as follows:

var fn = restArguments(function (a, b, args) {
  console.log(args);  / / / three, four, five
});
fn(1.2.3.4.5);
Copy the code

Before implementing the first version of the code, let’s take a look at function.length

Function.length – Number of parameters in a Function

Length is an attribute value of the function object. It is the number of parameters that the function must pass in.

The number of parameters does not include the number of remaining parameters, only the number of parameters before the first one has a default value.

In contrast, arguments.length is the number of actual arguments passed when the function is called.

// The length property of the Function constructor is 1
console.log(Function.length);  / / 1

// The property Writable: false, Enumerable: false, different: true
console.log(Object.getOwnPropertyDescriptor(Function.'length')); 
// {value: 1, writable: false, enumerable: false, configurable: true}

Prototype The length property of the object is 0
console.log(Function.prototype.length);  / / 0

console.log((function(){}).length);   / / 0
console.log((function(a, b){}).length);  / / 2

// The number of parameters does not include the remaining parameters
console.log((function(. args) {}).length);  / / 0

// Only the number of arguments before the first one has a default value
console.log((function(a, b = 1, c) {}).length);  / / 1
Copy the code

The first version implements:

function restArguments(func) {
  return function() {
    // startIndex indicates where to use the parameter to store the remaining parameters
    // By default, the last argument of the passed function is used to store the remaining arguments
    var startIndex = func.length - 1;
    var length = arguments.length - startIndex,
        rest = Array(length),
        index = 0;
    // Use an array to store the remaining parameters
    // In the example above, the rest result is [3, 4, 5]
    for (; index < length; index++) {
      rest[index] = arguments[index + startIndex];
    }
    var args = Array(startIndex + 1);
    // args此时为[1, 2, undefined]
    for (index = 0; index < startIndex; index++) {
      args[index] = arguments[index];
    }
    // args is [1, 2, [3, 4, 5]]
    args[startIndex] = rest;
    return func.apply(this, args);
  };
}
Copy the code

Optimized final version

Point of optimization:

  • Add parameter startIndex to indicate which parameter to store the remaining parameters from
  • Consider the number of remaining arguments
  • For performance reasons, use Call when there are few parameters
function restArguments(func, startIndex) {
  startIndex = startIndex == null ? func.length - 1 : +startIndex;
  return function() {
    var length = Math.max(arguments.length - startIndex, 0),
        rest = Array(length),
        index = 0;
    for (; index < length; index++) {
      rest[index] = arguments[index + startIndex];
    }
    switch (startIndex) {
      case 0: return func.call(this, rest);
      case 1: return func.call(this.arguments[0], rest);
      case 2: return func.call(this.arguments[0].arguments[1], rest);
    }
    var args = Array(startIndex + 1);
    for (index = 0; index < startIndex; index++) {
      args[index] = arguments[index];
    }
    args[startIndex] = rest;
    return func.apply(this, args);
  };
}
Copy the code

Testing:

var fn = restArguments(function(a, b, c, d) {console.log(a, b, c, d); },5);
fn(1.2.3.4);  // 1, 2, 3, 4

var fn1 = restArguments(function(a, args) {console.log(a, args); }); fn1();// undefined []

var fn2 = restArguments(function(a, b, c, d) {console.log(a, b, c, d); },2);
fn2(1.2.3.4);  // 1 2 [3, 4] undefined
Copy the code

Reference: github.com/mqyqingfeng… Developer.mozilla.org/zh-CN/docs/… Juejin. Cn/post / 684490…