JS create array with arrow function and rest parameters - javascript

In one app I saw this part of code:
const arrayCreate = length => [...Array(length)];
It works, but I'm not sure how. We have a arrow function that takes a length parameter, and creates an array in array or what? :)
And what does the spread operator doing here?

Array(length) will create an array of length empty slots, for which, though, you cannot map or iterate using array methods like .map .forEach etc.
When you spread (...) the array with the empty slots it will create a new array with undefined for each array position. So you can now .map and .forEach because they are not empty slots.
So this is a way to create an array of length filled with undefined.
You could do the same with
Array(length).fill()
or
Array.from(Array(length))

#Gabrielse's post answers the question pretty well. However, there's just one more thing that I'd like to add to it.
One use-case where you might do something like this is when you have a set iterable and you want its elements inside an array. for e.g.
const set = new Set();
set.add('foo');
set.add('bar');
set.add('baz');
Suppose, you've the items foo, bar, baz in a set. A simple console.log(set); would result Set { 'foo', 'bar', 'baz' }. The result you get of course is a set. But what if you want these items in an array?
A simple way to do this would be to use ...(spread operator) applied to the set inside an array. The result of console.log([...set]); would be [ 'foo', 'bar', 'baz' ].

Interesting! The developer who made this probably wanted to optimize the performance at writing a first value at index or to be able to iterate through the empty values
An array is a javascript object that have:
key/value pairs assigned to respectively array's index/ value at index
length (size of the array)
prototype (getters, setters, array methods...)
Array(length) or new Array(length) returns { length: 0, prototype: {} }. The object is empty, with the length equals to passed in length
Now [...Array(length)] will return { 0: undefined, 1: undefined, 2: undefined: 3: undefined, 4: undefined, 5: undefined, length: 5, prototype: {} }. The properties are now allocated to their undefined values

Related

Object.keys() vs Array.prototype.keys()

let arr = new Array(3)
console.log(Object.keys(arr)) // []
console.log([...arr.keys()]) // [0,1,2]
Why is there a difference? It seems that an array in javascript can simultaneously have a length of 3 and be empty. How do you know the behavior of the different functions that operate on sparse lists?
Object.keys() does expect the parameter to be of type object and will cast anything else implicitly to one. Passing an empty array will be casted to an empty object I guess: Object.assign({}, new Array(3)) will give {}

Constructing Arrays using apply() vs new Array

I want to initialize an array of a given size with elements set to undefined by default.
new Array(5) returns an array of 5 empty items.
Array.apply(null, Array(5)) returns exactly what I want: a five element array with each element as undefined.
Why does the second option produce a different array value than the first?
This distinction is important because I can map over an array of empty items but I can for undefined elements.
new Array(5).map(Boolean) // [false, false, false, false, false]
Array.apply(null, Array(5)) // [ <5 empty items>]
.map is a native Array object method that is expected to return a value for every iteration over the array to create a new copied array from the array. Boolean can be used as a constructor function to create a boolean object with a true or false value. Try the same thing with Number like:
new Array(5).map(Number)
In your second example, .apply calls a function with a supplied context value as the first parameter, and the parameter the function takes in as the second parameter. Also here, Array can be used as a constructor function, to construct a new Array (just like with Number and Boolean of their respective types)
First of All, Javascript array is a Object. If you check this, you will understand it. ONly specialty is,it has length property that regular object in javascript.
var array = new Array(5)
typeof array
And other thing is Array.apply mean, it will call array native function with setting given context which is null for your case and parameters are in another array.
Array.apply(null, [,,,,,]);
output will be [undefined, undefined, undefined, undefined, undefined]
I personally would use Array.from
console.log(Array.from({ length: 5 }))
// if you want to initialise the values you can add a constructor function
console.log(Array.from({ length: 5 }, (_, index) => index + 1))

When can an empty array be iterated over?

Defining arrays in javascript using Array(n) defines an empty array, please correct me if I am wrong. Now if I write the below code:
Array(2).map((val) => console.log(val))
.. nothing happens, but when I spread out the elements of the array into an other array, like so:
[...Array(2)].map((val) => console.log(val))
.. I get undefined console logs. Can anyone please help me understand what is happening here ?
Without spreading, the array is simply an object with a length property, with no items populated:
{ length: 2, proto: Array(0) }
When you spread (...) into an array, if the object you're spreading has a length property, it will spread that many items, starting from obj[0], obj[1], etc. The object in question doesn't actually need to have those numeric properties - if it doesn't, then the result of the spreading will be an array of undefineds:
console.log([...Array(2)]);
An array of undefineds can be iterated over - here, the indicies 0 and 1 do exist in the array object, they just happen to not be assigned to anything.
const arr = [undefined, undefined];
console.log(1 in arr);
So, in short, it's because the difference between an array such as
{ length: 2 } // no elements to iterate over
and
{ length: 2, 0: undefined, 1: undefined }
Note that you shouldn't use map if you're not using the transformed array that .map returns - otherwise, if you just want to iterate over each item and console.log it, for example, you should use forEach.
This is because the spread operator takes the iterable's length, then iterates over the passed value (See spec).
Since all returned valued from iterating over Array(2) are undefined, your second line maps over [undefined, undefined], while your fist line maps over [empty x 2]. Note that "empty" aren't actual values.
map doesn't iterate over "empty" entries, like those created by Array(2), but does iterate over undefined values:
Because Array(2) doesn't populate the array. But it does set the length property to 2. So in the first instance, map has nothing to iterate over. But the destructuring creates an array of 2 elements of undefined, which allows map to work since undefined is a valid primitive.
This is how Array.map works - please read here: Array.prototype.map()
callback is invoked only for indexes of the array which have assigned
values, including undefined. It is not called for missing elements of
the array (that is, indexes that have never been set, which have been
deleted or which have never been assigned a value).

Array.apply(null, Array(9)) vs new Array(9) [duplicate]

What exactly is the difference between:
Array(3)
// and
Array.apply(null, Array(3) )
The first returns [undefined x 3] while the second returns [undefined, undefined, undefined]. The second is chainable through Array.prototype.functions such as .map, but the first isn't. Why?
There is a difference, a quite significant one.
The Array constructor either accepts one single number, giving the lenght of the array, and an array with "empty" indices is created, or more correctly the length is set but the array doesn't really contain anything
Array(3); // creates [], with a length of 3
When calling the array constructor with a number as the only argument, you create an array that is empty, and that can't be iterated with the usual Array methods.
Or... the Array constructor accepts several arguments, whereas an array is created where each argument is a value in the array
Array(1,2,3); // creates an array [1,2,3] etc.
When you call this
Array.apply(null, Array(3) )
It get's a little more interesting.
apply accepts the this value as the first argument, and as it's not useful here, it's set to null
The interesting part is the second argument, where an empty array is being passed in.
As apply accepts an array it would be like calling
Array(undefined, undefined, undefined);
and that creates an array with three indices that's not empty, but have the value actually set to undefined, which is why it can be iterated over.
TL;DR
The main difference is that Array(3) creates an array with three indices that are empty. In fact, they don't really exist, the array just have a length of 3.
Passing in such an array with empty indices to the Array constructor using apply is the same as doing Array(undefined, undefined, undefined);, which creates an array with three undefined indices, and undefined is in fact a value, so it's not empty like in the first example.
Array methods like map() can only iterate over actual values, not empty indices.
The .map() API does not iterate over completely uninitialized array elements. When you make a new array with the new Array(n) constructor, you get an array with the .length you asked for but with non-existent elements that will be skipped by methods like .map().
The expression Array.apply(null, Array(9)) explicitly populates the newly-created array instance with undefined, but that's good enough. The trick is whether or not the in operator will report that the array contains an element at the given index. That is:
var a = new Array(9);
alert(2 in a); // alerts "false"
That's because there really is no element at position 2 in the array. But:
var a = Array.apply(null, Array(9));
alert(2 in a); // alerts "true"
The outer call to the Array constructor will have explicitly populated the elements.
This is an artifact of how apply works. When you do:
new Array(9)
an empty array is created with a length of 9. map does not visit non–existent members, so does nothing at all. However, apply turns the array into a list using CreateListFromArrayLike so it turns the formerly empty array into a parameter list like:
[undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined];
that is passed to Array to create an array with 9 members, all with a value of undefined. So now map will visit them all.
BTW, ECMAScript 2015 has Array.prototype.fill for this (also see MDN) so you can do:
Array(9).fill(0);
Because the first array would not have ordered properties arr[0] === undefined and the second does. Array functions like forEach and map will iterate from 0 to the array's length - 1 and the lack of order to the properties of the first is an issue. The second version produces an array with the correct ordering, i.e.
arr = Array.apply(null, Array(3));
arr[0] === undefined //true
arr[1] === undefined //true
//etc.
The first version as you noticed doesn't. Also, adding new to the first version would not make it work.
In the first case you have one operation
Array(3)
Its creates an array with three empty slots. Not an array with the three undefined values but exactly - empty.
At the second case
Array.apply(null, Array(3) )
we can spread it to the three operations:
first: Array(3) - you get an array with 3 empty slots;
second: Array(3) spreads by Function.prototype.apply() function to 3 parameters that it passes to Array() function. At this stage 3 empty slots in given array transformes by apply() to 3 undefined values (it looks like if apply() sees an empty slot it automaticaly turns it to undefined in any sparsed array).
third: we get an Array(undefined, undefined, undefined). And that will do to us an array with 3 undefined (not empty) values.
Because now you have 3 undefined but not empty slots, you can use them with map() function.
Note that not only Function.prototype.apply() have such behavior of decomposing arrays by such way. You can also do this in ECMAScript 6 by "..." - spread operator.
Array(...new Array(3));
This will also returns an array with 3 undefined and respectively can be mapped slots.
Here i giving more detailed explanation.
https://stackoverflow.com/a/56814230/11715665

Javascript arrays?

This may be completely stupid but given I type this into my browser console:
var arr = [];
arr.item = 'val';
console.log( arr );
arr;
What is the arr; syntax doing behind the scenes? I'm assuming console.log( arr ); is iterating all the properties of the arr object but what is arr; doing? Also does [] tell me I'm dealing with an object of type array and {} tells me I'm dealing with an object of type object literal? So [ item: 'val' ] is an object with a prototype of array and { item: 'val' } is an object of prototype object literal?
Edit: Another way to ask this might be why are console.log and arr; different?
Sorry, started to type the comment and realized it was actually a full answer.
console.log yields an implementation dependent string representation of the argument. It does not iterate the properties of an object in most I've seen. String serializtions using the default toString method look like [object Foo] where 'Foo' is the internal class property of the object. You can see this for arrays by using Object.prototype.toString.call([]);.
The last line on the other hand, is just the value. How that gets represented as an evaluated expression in your browser console is implementation-dependent, but for all intents and purposes it does nothing and there is no reason you would want to write that.
Note that adding a property to an array with the . operator is almost never what you want: it does not add to the contents of the array but treats the array as an object. Iterating the indicies of the array will not yield that property, nor will its methods such as .map include it. Use .push. If you want string-keyed properties, use an object.
What is the arr; syntax doing behind the scenes?
This is an ExpressionStatement. The JavaScript engine tries to evaluate the expression by reading the value of the variable, and then does nothing with it. If it occures inside a javascript function, it will do nothing (except when arr is not a variable, it will throw an error). If you just write arr; into the developer console, most browsers will echo the value of the last typed in expression, so you will see the value of arr as a result. The echoing mechanism is often implemented similar to console.log;
I'm assuming console.log( arr ); is iterating all the properties of the arr object
console.log prints a value to the developer console. If the value is a string, then the string gets printed. If the value is an object, then it is browser-dependent what happens. Most browsers, like Chrome will show an interactive representation of the object, like this:
Other browser might just call toString on the object, which in fact will iterate over the elements, and join them by commas.
Also does [] tell me I'm dealing with an object of type array and {} tells me I'm dealing with an object of type object literal?
Yes and yes. [1, 2, 3] is an array literal, which will evaluate to a new instance of an array containing those values. [] is just an empty array. Arrays are instances of the type Array. {a:1,b:2} is an object literal, will evaluate to a new instance every time. {} is an empty object with no fields. Object literals evaluate to instances of the type Object.
So [ item: 'val' ] is an object with a prototype of array and { item: 'val' } is an object of prototype object literal?
No and yes. [ item: 'val' ] is invalid syntax. Array literals have elements separated by commas, they cannot have colons in them.
[ item: 'val' ]
VM154:1 Uncaught SyntaxError: Unexpected token :(…)
The : is the unexpected token, the expression can't be evaluated because it has syntax errors.
On the other hand { item:'val' } is a completely regular object literal with type Object and will have a single field named item with value 'val'.
About object properties
In javascript, object do not have a fixed set of properties. Properties can be dynamically added and removed by writing them and by using the delete keyword. Every object has string (or Symbol) field names. An array can be seen as an object, optimized for integer keys. But still, keys are strings, so a[0] means the same as a["0"].
var arr = [1, 2];
arr.item = 'val'; // assigns a new field to the object, called item
arr['item'] = 'val'; // does exactly the same as the previous
arr[1] = 3; // sets the second item in arr to 3
arr['1'] = 3; // does exactly the same as the previous
console.log(arr);
// prints [1, 2, item: "val"]
So arbitrary named fields can be assigned on an array, and when printed, Chrome does notice the object being an array, so uses array notation, but also prints the 'other' assigned values. It's important to mention that not every field gets printed, as arr.length is a fully featured property. It is the way guys at Google chose to print arrays. It is not a valid JavaScript syntax, it is just a (pretty good) visualisation of data inside an array.
About classes and prototypes
We say that foo is of class Foo if foo has the prototype Foo.prototype, in other words foo.__proto__ === Foo.prototype. Here foo is an object, Foo is a constructor function. As Foo itself is a function, Foo.__proto__ === Function.prototype.
If subtypes are involved, then we can say bar is an instance of Foo, if bar has Foo.prototype in it's prototype chain, i.e.: bar.__proto__ === Foo.prototype || bar.__proto__.__proto__ === Foo.prototype || .... Explicitly accessing an object's .__proto__ property is considered a bad practice and is strongly discouraged. Altering the __proto__ value leads to serious performance drop, because it breaks the browser's optimisation strategies. So how to check if an object is of a type? You can use the instanceof operator.

Categories

Resources