JavaScript “new Array(n)” and “Array.prototype.map” weirdness

后端 未结 14 1896
粉色の甜心
粉色の甜心 2020-11-22 02:40

I\'ve observed this in Firefox-3.5.7/Firebug-1.5.3 and Firefox-3.6.16/Firebug-1.6.2

When I fire up Firebug:

相关标签:
14条回答
  • 2020-11-22 03:14

    With ES6, you can do [...Array(10)].map((a, b) => a) , quick and easy!

    0 讨论(0)
  • 2020-11-22 03:16

    ES6 solution:

    [...Array(10)]
    

    Doesn't work on typescript (2.3), though

    0 讨论(0)
  • 2020-11-22 03:17

    Just ran into this. It sure would be convenient to be able to use Array(n).map.

    Array(3) yields roughly {length: 3}

    [undefined, undefined, undefined] creates the numbered properties:
    {0: undefined, 1: undefined, 2: undefined, length: 3}.

    The map() implementation only acts on defined properties.

    0 讨论(0)
  • 2020-11-22 03:17

    Here's a simple utility method as a workaround:

    Simple mapFor

    function mapFor(toExclusive, callback) {
        callback = callback || function(){};
        var arr = [];
        for (var i = 0; i < toExclusive; i++) {
            arr.push(callback(i));
        }
        return arr;
    };
    
    var arr = mapFor(3, function(i){ return i; });
    console.log(arr); // [0, 1, 2]
    arr = mapFor(3);
    console.log(arr); // [undefined, undefined, undefined]

    Complete Example

    Here's a more complete example (with sanity checks) which also allows specifying an optional starting index:

    function mapFor() {
    var from, toExclusive, callback;
    if (arguments.length == 3) {
        from = arguments[0];
        toExclusive = arguments[1];
        callback = arguments[2];
    } else if (arguments.length == 2) {
        if (typeof arguments[1] === 'function') {
            from = 0;
            toExclusive = arguments[0];
            callback = arguments[1];
        } else {
            from = arguments[0];
            toExclusive = arguments[1];
        }
    } else if (arguments.length == 1) {
        from = 0;
        toExclusive = arguments[0];
    }
    
    callback = callback || function () {};
    
    var arr = [];
    for (; from < toExclusive; from++) {
        arr.push(callback(from));
    }
    return arr;
    }
    
    var arr = mapFor(1, 3, function (i) { return i; });
    console.log(arr); // [1, 2]
    arr = mapFor(1, 3);
    console.log(arr); // [undefined, undefined]
    arr = mapFor(3);
    console.log(arr); // [undefined, undefined, undefined]

    Counting Down

    Manipulating the index passed to the callback allows counting backwards:

    var count = 3;
    var arr = arrayUtil.mapFor(count, function (i) {
        return count - 1 - i;
    });
    // arr = [2, 1, 0]
    
    0 讨论(0)
  • 2020-11-22 03:19

    In ECMAScript 6th edition specification.

    new Array(3) only define property length and do not define index properties like {length: 3}. see https://www.ecma-international.org/ecma-262/6.0/index.html#sec-array-len Step 9.

    [undefined, undefined, undefined] will define index properties and length property like {0: undefined, 1: undefined, 2: undefined, length: 3}. see https://www.ecma-international.org/ecma-262/6.0/index.html#sec-runtime-semantics-arrayaccumulation ElementList Step 5.

    methods map, every, some, forEach, slice, reduce, reduceRight, filter of Array will check the index property by HasProperty internal method, so new Array(3).map(v => 1) will not invoke the callback.

    for more detail, see https://www.ecma-international.org/ecma-262/6.0/index.html#sec-array.prototype.map

    How to fix?

    let a = new Array(3);
    a.join('.').split('.').map(v => 1);
    
    let a = new Array(3);
    a.fill(1);
    
    let a = new Array(3);
    a.fill(undefined).map(v => 1);
    
    let a = new Array(3);
    [...a].map(v => 1);
    
    0 讨论(0)
  • 2020-11-22 03:19

    Not a bug. That's how the Array constructor is defined to work.

    From MDC:

    When you specify a single numeric parameter with the Array constructor, you specify the initial length of the array. The following code creates an array of five elements:

    var billingMethod = new Array(5);
    

    The behavior of the Array constructor depends on whether the single parameter is a number.

    The .map() method only includes in the iteration elements of the array that have explicitly had values assigned. Even an explicit assignment of undefined will cause a value to be considered eligible for inclusion in the iteration. That seems odd, but it's essentially the difference between an explicit undefined property on an object and a missing property:

    var x = { }, y = { z: undefined };
    if (x.z === y.z) // true
    

    The object x does not have a property called "z", and the object y does. However, in both cases it appears that the "value" of the property is undefined. In an array, the situation is similar: the value of length does implicitly perform a value assignment to all the elements from zero through length - 1. The .map() function therefore won't do anything (won't call the callback) when called on an array newly constructed with the Array constructor and a numeric argument.

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