I saw an interview question before I went to bed last night, which triggered a series of expansion and thinking.

The interview questions

Instead of using the loop, create an array of length 100 with each element equal to its subscript

Here are some of my solutions

Array.from(Array(100).keys())
[...Array(100).keys()]
Object.keys(Array(100))
Array.prototype.recursion = function(length) {
    if (this.length === length) {
        return this;
    }
    this.push(this.length);
    this.recursion(length);
}
arr = []
arr.recursion(100)
Array(100).map(function (val, index) {
    return index;
})Copy the code

There is, of course, another way to kill yourself

var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]Copy the code

You could get shot by the examiner! But there’s no loop either.

Questions and Reflections

The problem

Keys (Array(100)) returns an empty Array []

[undefined × 100]

thinking

A preliminary guess is related to undefined

arr = [];
arr[10] = 1;
arr[20] = 2;
Object.keys(arr)  //["10", "20"];
arr.map(function (val, index) {
    return index;
}); //  [undefined × 10, 10, undefined × 9, 20]Copy the code

You can see that undefined x 10 is displayed. This display represents the number of uninitialized values in the array, rather than 10 undefined values. In other words, the following formula is not equivalent, as we will see later, the former is a sparse array, and the latter is a dense array.

Array(5) // [undefined * 5] [undefined, undefined, undefined]Copy the code

That is, an Array created using the Array() constructor looks like this, and the two are equivalent.

Array(5)
[,,,,,]Copy the code

The problem is obvious because object.keys () and map skip empty parts of the array. In other words, uninitialized parts of the array are skipped. Once the problem is found, it’s easy to fix, just initialize the array with fill in ES6.

Object.keys(Array(100).fill(0))
Array(100).fill(0).map(function (val, index) {
    return index;
})Copy the code

expand

After solving the problem, I found a problem. All the above answers except recursion used ES6 method, so how to solve it in ES5? Consulting related materials found two terms sparse array and dense array. A brief summary of what it means:

  • Sparse array: A discontinuous array
  • Dense array: Contiguous array
Sparse = [] // Sparse [1] = 1SPARSE [10] = 10dense = [1, 2, 3, 4, 5] // DenseCopy the code

The key to creating a dense array is that we’re going to assign value to each index in the array like this

Sparse = [] // Sparse [1] = 1 [10] = 10 dense = [1, 2, 3, 4, 5] // DenseCopy the code

This creates an array with a value of “, not null, which is preferred.

You can also use apply, such as creating a dense Array Array of length 5. Apply (null, Array(5))

Actual equivalent for Array (,,,, undefined undefined undefined undefined undefined).

If we were considering ES6, we could also create dense arrays this way

Array.from({length:5})
Array(5).fill(undefined)Copy the code

The value of an array is undefined, but it is a dense array. In other words, sparse and dense arrays have nothing to do with the value of the array except whether or not each value in the array is initialized.

sparse = [] sparse[1] = 1 sparse[10] = 10 for (let index in sparse) { console.log('sparse:' + 'index=' + index + ' value=' + sparse[index]) } dense = Array.apply(null, Array(5)) for (let index in dense) { console.log('dense:' + 'index=' + index + ' value=' + dense[index]) } sparse[0] ===  dense[0]Copy the code

The results for

sparse:index=1    value=1
sparse:index=10    value=10
dense:index=0    value=undefined
dense:index=1    value=undefined
dense:index=2    value=undefined
dense:index=3    value=undefined
dense:index=4    value=undefined
trueCopy the code

We can see that the value in dense is undefined, but it is not for… True for sparse[0] === dense[0] means undefined. The only difference is undefined (uninitialized). Dense is undefined that we assigned (initialized).

In other words, we assign undefined, but because we do this assignment, js considers the array initialized and will not be used by the for… In skip.

Therefore, it can be seen that the array correlation method in JS has different processing methods for empty Spaces (sparse array and dense array). Here, the analysis is found in the empty space of array in Teacher Ruan’s ES6.

ES5’s treatment of empty seats is already quite inconsistent, mostly ignoring empty seats.

  • ForEach (), filter(), every(), and some() all skip the empty space.
  • Map () skips the empty space but keeps the value
  • Join () and toString() treat empty Spaces as undefined, while undefined and null are treated as empty strings.

ES6 explicitly converts empty space to undefined

  • The array. from method converts the empty space of the Array to undefined, which means it doesn’t ignore the empty space.
  • Extended operators (…) It also changes the empty space to undefined.
  • CopyWithin () copies the empty space.
  • Fill () treats the empty space as a normal array position.
  • The for… The of loop also iterates over the empty space.
  • Entries (), keys(), values(), find() and findIndex() treat empty Spaces as undefined

In short, because the rules for dealing with empty arrays are very inconsistent, it is recommended to avoid empty arrays.

In fact, typeof Array() can be found as object. The Array in JS is a special object. In other words, the Array in JS is not an Array in the traditional sense.

arr = []
arr.length // 0
arr[0] = 1
arr.length // 1
arr[100] = 100
arr.length // 101
arr[100] === arr['100'] // true
arr['a'] = 1
arr.length // 101Copy the code

In addition, when assigning a value to the array, if the index of the numeric index exceeds the original length of the array, the length of the array will be automatically expanded to index + 1, and the gap in the middle will be automatically filled with undefined. The array becomes sparse. When index is not numeric, the value is still filled in, but the length does not change.

Two conditions must be met for the length of an array to vary by interpolation

  • isNaN(parseInt(index,10)) === false
  • index >= length

Now, what happens to the values in the array when the length of the array changes?

arr = [1, 2, 3, 4, 5, 6, 7]
arr['test'] = 1
console.log(arr) // [1, 2, 3, 4, 5, 6, 7, test: 1]
arr.length = 1
console.log(arr) // [1, test: 1]Copy the code

As can be seen, if the changed length is less than the original length, all index(including string numbers, which are equivalent in the array index) greater than or equal to length will be deleted, that is, all the following two conditions will be deleted

  • isNaN(parseInt(index,10)) === false
  • index >= length

conclusion

Instead of using a loop, create an array of length 100 with each element equal to its subscript

The answer

Array.from(Array(100).keys())
[...Array(100).keys()]
Object.keys(Array(100).join().split(','))
Object.keys(Array(100).fill(undefined))
Object.keys(Array.apply(null,{length:100}))
Array.prototype.recursion = function(length) {
    if (this.length === length) {
        return this;
    }
    this.push(this.length);
    this.recursion(length);
}
arr = []
arr.recursion(100)
Array(100).fill(0).map(function (val, index) {
    return index;
})Copy the code

The result Array of object.keys () is a string of numbers, and the map code in array.prototype.map () polyfill on MDN also uses a loop. But what the writer means is that he does not use for… In, for… Loop of, for, while.

All in all, I think the safest answers are the following

Array.from(Array(100).keys())
[...Array(100).keys()]
Array.prototype.recursion = function(length) {
    if (this.length === length) {
        return this;
    }
    this.push(this.length);
    this.recursion(length);
}
arr = []
arr.recursion(100)Copy the code