Mapping Array in Javascript with sequential numbers

后端 未结 4 1861
逝去的感伤
逝去的感伤 2021-02-04 17:40

The following code:

let myArray = Array.apply(null, {length: 10}).map(Number.call, Number);

Creates the following Array:

[0, 1,         


        
相关标签:
4条回答
  • 2021-02-04 18:25

    es6 simpliedfied version

    let k = Array.from({ length: 5 }).map((currentElement, i) => i)
    
    console.log(k)
    
    // Output -[0, 1, 2, 3, 4]

    0 讨论(0)
  • 2021-02-04 18:26

    Let's decompose the expression in two parts:

    1) Let's discuss the first expression:

    Array.apply(null, {length: 10})
    

    In JavaScript the Array constructor can take a parameter to create an array of a certain length, like:

    Array(10) // makes an array of length 10
    

    This array is a sparse array (an array which contains indexes with no elements). You can imagine we generated the following array:

    [,,,,,,,,] // Array with 10 indexes, but no elements
    

    You can think of an array in JavaScript as an object that has a length property and numbered indexes. For example, the following is a valid representation of an array:

    var arr = {length: 3, 0: 1, 1: 2, 2: 3} // this represents the array [1, 2, 3]
    

    In JavaScript we call this object an "array-like object". You can iterate this object with a regular for loop:

    for (var i=0; i<arr.length; i++) {
      console.log(arr[i]) // logs 1 .. 2 .. 3
    }
    

    But this object is not an instance of the Array constructor:

    arr instanceof Array // false
    

    Fortunately, any array-like object can be converted to an array:

    Array.prototype.slice.call({length: 3, 0: 1, 1: 2, 2: 3}) // [1, 2, 3]
    

    All array methods are intentionally generic to allow this behavior, so you can easily loop with forEach for example:

    Array.prototype.forEach.call({length: 3, 0: 1, 1: 2, 2: 3}, function(x) {
      console.log(x)
    })
    

    Now, back to the first expression:

    Array.apply(null, {length: 10})
    

    Decomposing the above expression knowing about array-like objects, we can see that is equivalent to:

    Array(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
    

    In other words, we are creating an array of 10 elements with a value of undefined (notice it is not sparse anymore)

    2) Going into the second expression:

    .map(Number.call, Number);
    

    The first argument is a callback function to apply to each element in the array, the second argument is the this value inside the callback.

    Let's decompose this expression. First we can write out the callback function as an anonymous function:

    Array.apply(null, {length: 10}).map(function() {
      return Number.call.apply(this, arguments)
    }, Number)
    

    Then we realize that Number.call is a shorthand for Function.prototype.call:

    Array.apply(null, {length: 10}).map(function() {
      return Function.prototype.call.apply(this, arguments)
    }, Number)
    

    Next, we inline the this value:

    Array.apply(null, {length: 10}).map(function() {
      return Function.prototype.call.apply(Number, arguments)
    })
    

    And finally we decompose the application of the function:

    Array.apply(null, {length: 10}).map(function() {
      return Number.call(arguments[0], arguments[1], arguments[2]) // map provides 3 arguments
    })
    

    As you can see, the first argument, which is the element, which is undefined is the this value of the call to Number, which is to say we discard it. The second argument is the index, which is the value we care about, and the third argument is not needed because Number only takes one argument, so this one is discarded as well.

    In this case the Number function is used as the identity function:

    function id(x) {
      return x
    }
    

    It is merely a function with one parameter that returns the argument passed into it. That's all we care about. Because index is already a number, we get:

    Number(index) === id(index)
    

    Hope that helps further understanding.

    Edit: To expand on the reason why Array(10) won't work with iteration methods such as map versus Array.apply(null, {length: 10}) we have to look at the implementation of map (scroll to the "Polyfill" heading).

    The reason is because as I pointed out before, Array(10) is a sparse array, it does not have any values in it, just a length. By looking at the implementation we can see what is happening:

    // 8. Repeat, while k < len
    while (k < len) {
    
      var kValue, mappedValue;
    
      // a. Let Pk be ToString(k).
      //   This is implicit for LHS operands of the in operator
      // b. Let kPresent be the result of calling the HasProperty internal 
      //    method of O with argument Pk.
      //   This step can be combined with c
      // c. If kPresent is true, then
      if (k in O) {
    
        // i. Let kValue be the result of calling the Get internal 
        //    method of O with argument Pk.
        kValue = O[k];
    

    You can see that in k in O the in operator checks for existence first, and the value does not exist; it is not undefined, it is just not present. This is not the same as just doing property access like O[k] where it would give you a value of undefined if the property doesn't exist.

    var o = {}
    
    'p' in o // false
    o.p // undefined
    
    0 讨论(0)
  • 2021-02-04 18:26
    Array.apply(null, {length: 10}) 
    

    //> it outputs the array of undefined, with the length of ten, why: it takes null for execution because it does not need this for execution, besides this argument must be tepeof object, and as we know: typeof null === 'object', if you will place any other object instead of null, it will do the same thing, it matters only what is the length of the second argument( it tests the length of the second argument),

    .map(Number.call, Number);

    decompose(we know that Number constructor is Function.prototype(Number.proto), so):

    .map(function(){
    Function.prototype.call.apply(this, arguments)
    }, Number)// the second arg is the this value that map function takes during each iteration
    

    We also know that this is Number, cause every time it calls Number as this:

    Function.prototype.call.apply(Number, arguments)
    

    });//here we don't need second argument

    Now we compose everything again: Function.prototype => Number,

    call.apply(Number, arguments); => call(arguments[0], arguments[1])
    

    reason: arguments is array-like object, we can still pass it to apply method, but we cannot to call, cause it takes arguments separated by comma.You cannot define arguments as an argument, so you need to indicate what it is looking for: index, and that is arguments[1], as this value it must take any object, so it take: null, or anything, but it must be present:

    return Number.call(null, arguments[1]);
    

    Here Number is an identity function:

    function example(x){
     return x;
    }
    

    so Number(e) == example(e); so on first iteration:

    Number(0) //it takes 0 index:
    
    return 0..
    

    then:

    Number(1)//cause it takes the index of the second element:
    
    return 1..
    

    Thanks for reading...

    0 讨论(0)
  • 2021-02-04 18:30
    Array.apply(null, {length: 10})
    

    creates an array of length 10 with all elements being undefined.

    .map(Number.call, Number)
    

    will invoke Number.call for each element with the arguments (element, index, array) and setting this to Number. The first argument to call will be taken as this (not relevant here), and all the other arguments are passed as they are, with the first one being the index. And Number will now convert its first argument, index, to a number (here: will return the index, as it is a number), and that's what map will write to its return array.

    0 讨论(0)
提交回复
热议问题