{"/book":1,"/order":2,"deliver":3}
In the UI of my app, When I click on /book, I know from the map, what step to go to.
At times I just want to go to the next step by incrementing the number but making sure URL also changes.
How do I do a reverse mapping from step to key.
I came across Object.keys introduced in ECMAScript 5.
Is the order of element in the returned list of keys always same ?
["/book","/order","deliver"]
If yes then why so ? Dictionaries are unordered right ?
The ECMAScript 5.1 specification states that
When the keys function is called with argument O, the following steps
are taken:
If the Type(O) is not Object, throw a TypeError exception.
Let n be the number of own enumerable properties of O
Let array be the result of creating a new Object as if by the expression new Array(n) where Array is the standard built-in
constructor with that name.
Let index be 0.
For each own enumerable property of O whose name String is P:
(a) Call the [[DefineOwnProperty]] internal method of array with arguments ToString(index), the PropertyDescriptor {[[Value]]: P,
[[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and
false.
(b) Increment index by 1.
Return array.
If an implementation defines a specific order of enumeration for the
for-in statement, that same enumeration order must be used in step 5
of this algorithm.
The Mozilla Development Network has this to say about the for-in loop:
A for...in loop iterates over the properties of an object in an arbitrary order (see the delete operator for more on why one cannot depend on the seeming orderliness of iteration, at least in a cross-browser setting).
And the reference to the delete operator leads to this:
Although ECMAScript makes iteration order of objects implementation-dependent, it may appear that all major browsers support an iteration order based on the earliest added property coming first (at least for properties not on the prototype). However, in the case of Internet Explorer, when one uses delete on a property, some confusing behavior results, preventing other browsers from using simple objects like object literals as ordered associative arrays. In Explorer, while the property value is indeed set to undefined, if one later adds back a property with the same name, the property will be iterated in its old position--not at the end of the iteration sequence as one might expect after having deleted the property and then added it back.
So if you want to simulate an ordered associative array in a cross-browser environment, you are forced to either use two separate arrays (one for the keys and the other for the values), or build an array of single-property objects, etc.
I think the conclusion to draw from this is that, within a single browser, the order will be arbitrary but consistent but if you compare multiple browsers then the order may differ across those browsers (especially if you are deleting and re-adding properties).
Edit:
If you want to sort the keys based on the associated value then you can do something like this:
var map = { b: 2, a: 1, c: 3 };
var keys = Object.keys( map );
console.log( keys ); // [ 'b', 'a', 'c' ]
var sorted_keys = keys.slice(0); // sliced so that we can see the difference in order
sorted_keys.sort( function( a, b ){ return map[a] - map[b]; } );
console.log( sorted_keys ); // [ 'a', 'b', 'c' ]
It is up to the implementation. From the ES5 spec on Object.keys:
If an implementation defines a specific order of enumeration for the for-in statement, that same enumeration order must be used in step 5 of this algorithm.
The "step 5" mentioned there does not specify an order:
5. For each own enumerable property of O whose name String is P...
Related
According to the ECMAScript spec, Array instances, which are exotic objects, inherit properties from the Array prototype object (ref). One of such properties is a well-known length. Changing it directly on an instance may result in removing certain index-keyed properties (ref).
On the other hand, as it is said elsewhere with respect to ordinary objects, prototype's data properties are not inherited for the purposes of set access. It means an attempt to set a property on a instance that has not such a key among its own properties keys causes creating a new own property with this key whether a prototype knows this key or not.
Due to I can't find a similar notion but conernced with Array exotic objects, I wonder what should happen when we doing something like this:
let arr = [0, 1, 2];
arr.length = 1;
Was an own property defned on the instance with the prototype's one keeping untouched or are we tinkering with the same length here (that of the prototype's)?
No, array instances do not share their length property with Array.prototype. These are distinct properties.
First of all, the specification indicates that Array.prototype is itself an Array -- which is why it has a length property. See Properties of the Array Prototype Object:
is an Array exotic object and has the internal methods specified for such objects.
has a "length" property whose initial value is +0
Secondly, when arrays a constructed, like with the Array function, the steps in ArrayCreate are executed:
ArrayCreate ( length [ , proto ] ):
[...]
6. Perform ! OrdinaryDefineOwnProperty(A, "length", PropertyDescriptor { [[Value]]: đť”˝(length), [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }).
This leaves no doubt that every constructed array has its own length property.
As an illustration, we can modify Array.prototype.length from its default value of 0 to another value, and then see that this does not influence the value of length of an instance that is created later:
console.log(Array.prototype.length); // 0
Array.prototype.length = 1;
console.log(Array.prototype.length); // 1
console.log(Array().length); // 0
This is my Array
let setOne = [""];
Now, I created a function that takes the element from the array i.e setOne, store that element in one Object as a property name and give that property-name a value of true and prints the object data.
Function is
function checkTheSameBetter(setOne) {
let ObjectShop = {};
for (let indexSetOne = 0; indexSetOne < setOne.length; indexSetOne++) {
ObjectShop[setOne[indexSetOne]] = true;
}
console.log(ObjectShop);
}
The Output which I get is
{ '': true }
No Problem so far
Here comes the main part
when I add another value to an array i.e setOne, consider "1".
let setOne = ["",1];
And then when I execute the function checkTheSameBetter. I get output
{'1': true, '': true }
So my question is, "how did that '1' get stored in the first
position?"
The output I expected was in this order
{'': true, '1': true}
Here is the sandbox Link
https://codesandbox.io/s/cranky-cori-qx116?file=/src/index.js
can anyone please tell me what's happening here?
If you need any clarification I will give it.
Thank You
JS Objects have traditionally been 'unordered' (until ES5). Since ES6, there is a predictable order to the Object properties iteration. It, however, isn't the 'insertion' order. The order of keys will be as follows:
First, all non-negative integer keys less than 232, in ascending order. (eg. '1', '79', etc. basically all valid array indices. Caveat: '05' wouldn't be considered integer key, since the integer parsed from it will yield a different string representation).
Then, all String keys, in the original order of insertion. (Numeric strings not falling within bounds of step one will be considered here.)
Then, all Symbol keys, in the original order of insertion.
Looking at the rules above, it makes sense that '1', being an integer key, appeared before '' (a string).
Please note that this only applies to ES2015 and later. To avoid confusion, and eye rolls from colleagues habituated of viewing objects as unordered, please don't rely on enumeration order of Object properties. If the enumeration order is relevant, you can always use Map which guarantees that insertion order will be maintained.
ES6 defines an order in which own properties of an object are enumerated. Following are the rules according to which own properties of an object are enumerated:
String properties whose names are non-negative numbers are listed first, from smallest to largest. This means that properties of array and array-like objects will be enumerated in order.
After that, all properties with string names are listed in the order they were added in the object. This also includes properties that look like non-negative numbers or floating point numbers.
At last, properties whose names are Symbols are listed in the order they were added in the object.
Following functions list the properties in the above described order, subject to their own constraints.
Object.keys()
Object.getOwnPropertyNames()
Object.getOwnPropertySymbols()
Reflect.ownKeys()
One thing to keep in mind is that enumeration order for for in loop is not as tightly specified as it is for above mentioned enumeration functions but it typically enumerates own properties in the order described above.
As for in loop also enumerates properties in the prototype chain, once own properties have been enumerated, it will then move up the prototype chain, enumerating properties of each prototype object in the same order as described above. Although if a property has already been enumerated, any property with the same name won't be enumerated again. Property won't be enumerated even if a non-enumerable property with the same name has already been considered.
const obj = {};
obj[2] = 2;
obj['-1'] = -1;
obj['1'] = 1;
obj['as'] = 'as';
obj['10'] = 10;
obj['b'] = 'b';
console.log(Reflect.ownKeys(obj));
console.log(Object.getOwnPropertyNames(obj));
for (const key in obj) {
console.log(key);
}
.as-console-wrapper { max-height: 100% !important; top: 0; }
I'm trying to learn Javascript - here's my issue:
In the w3schools.com javascript array examples, they show the sequent example:
var person = [];
person["firstName"] = "John";
person["lastName"] = "Doe";
person["age"] = 46;
document.getElementById("demo").innerHTML =
person[0] + " " + person.length;
An array "person" has been defined, but then they proceed to add some elements whit a "named" index. Then tries to print the HTML document the 0th element and the number of elements of the array, like you would do with a standard array.
The description says:
If you use a named index when accessing an array, JavaScript will
redefine the array to a standard object, and some array methods and
properties will produce undefined or incorrect results.
In fact, person[0] and person.length return respectively "undefined" and "0". Even is person was initially defined as an array, by inserting new named indexes elements, the array should be redefined as an object. But when i try do use the Array.isArray() method for checking it, it returns true:
var person = [];
person["firstName"] = "John";
person["lastName"] = "Doe";
person["age"] = 46;
document.getElementById("demo").innerHTML =
person[0] + " " + person.length;
document.getElementById('test').innerHTML = Array.isArray(person);// returns true
So, why? if, as specified by the tutorial, this has been effectively redefined as a standard object, and the ECMAScript 5 has added the .isArray() method for checking if something is an array and nothing else, shouldn't this return false insted of true?
I'm sure i missed something. If i define person like this:
person = {};
then it returns false, as expected. What is happening here? I just wanted to understand arrays a little bit more, this confuses me. Is this just a broken array, but still an array?
Here's the example (without the Array.isarray() bit, just the default): https://www.w3schools.com/js/tryit.asp?filename=tryjs_array_associative_2
First of all I want to note that the example you took from the w3schools page on arrays, is from the "Associative Arrays" section, which has this important introduction:
Many programming languages support arrays with named indexes.
Arrays with named indexes are called associative arrays (or hashes).
JavaScript does not support arrays with named indexes.
In JavaScript, arrays always use numbered indexes.
This puts the example into context, because it really makes no sense to define a variable as an array and then use string keys. But this was an example to illustrate the point.
Does an Array become an Object?
That JavaScript still considers the variable to be an array is as expected. It becomes an array at the moment of assignment of [], and that does not change by adding properties to that object. Yes, arrays are objects. They just have additional capabilities.
The array did not lose any of its array-like capabilities, but those features just don't work on those string properties, ... only on numerical ones (more precisely, the non-negative integer ones).
You loosely quoted the following statement from w3schools:
If you use named indexes, JavaScript will redefine the array to a standard object.
That is wrong information and leads to your misunderstanding. There is no redefinition happening. When you add properties to any object, then the object does not change "type". It remains an instance of what it was before... An array remains an array, a date object remains a date, a regex object remains a regex, even if you assign other properties to it. But non-numerical properties do not "count" for an array: the length will remain unchanged when you add such properties. The length only reveals something about the numerical properties of the object.
This quote is yet another illustration of what the JavaScript community thinks about w3schools.com, i.e. that it is not the most reliable reference, even though it has its value for learning the language.
Example of adding useful properties to arrays
Having said the above, there are cases where you may intentionally want to make use of such properties on arrays. Let's for example think of an array of words that is sorted:
const arr = ["apple", "banana", "grapefruit", "orange", "pear"];
Now let's add something to this array that denotes that it is currently sorted:
arr.isSorted = true;
We could imagine a function that would allow one to add a value to this array, but which also verifies if the array is still sorted:
function addFruit(arr, fruit) {
if (arr.length && fruit < arr[arr.length-1]) {
arr.sorted = false;
}
arr.push(fruit);
}
Then after having added several values, it would maybe be interesting to verify whether the array needs sorting:
if (!arr.sorted) arr.sort();
So this extra property helps to avoid executing an unnecessary sort. But for the rest the array has all the functionality as if it did not have that extra property.
An object that is set up as an array and then filled as an object becomes a member of both classes. Methods of the Array class will apply to its 'array-ness':
Array.isArray(person);
returns true. Methods of the Object class will apply to its 'object-ness':
typeof(person);
returns object. When it could be either one, the 'array-ness' will prevail, because the variable was first defined as an array:
console.log(person);
will put Array [ ] on the console, because it runs the Array class's logging method. It is displayed as an empty array, since it has no numbered elements, but you could add some:
person[2]=66;
and then console.log would log Array [ <2 empty slots>, 66 ].
I think the polyfill implementation of isArray() will clear your doubt by some extent.
#Polyfill
How can you prove that an array’s indices are just the enumerable properties? I know, it shows up during a for in loop, but what I mean is: how can I prove that an array’s index is a property? And also an enumerable property?
Are they identical to general Object properties?
You can prove it’s an enumerable property pretty easily. As you said, it shows up in a for in loop. That’s the definition of “enumerable”. If you want another way, though:
var a = ['hello'];
Object.getOwnPropertyDescriptor(a, '0')
// {value: 1, writable: true, enumerable: true, configurable: true}
And yes, they’re like any other object property, except for that they change an array’s length if one past the end is created. That’s the only different thing about arrays.
var a = [];
a[0] = 5;
a.length // 1
Assuming I've understood your question correctly, yes, array indices are effectively the same as object properties. When you set the property of an object, the internal [[DefineOwnProperty]] function runs. The specification gives a modified version of that function that is used when dealing with Array objects.
After various checks (to ensure the property identifier is a valid array index for example), it does the following:
5. Return the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing P, Desc, and Throw as arguments
Which is exactly what happens for "normal" objects.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Are Javascript arrays sparse?
I am learning JavaScript at the moment and have been reading some simple introductions and tutorials. While looking at the Array object I stumbled upon some details, which strike me as very odd, coming from other languages like C/Java/Scala/...
So lets assume we define an array as such:
var arr = ['foo','bar','qux']
We now assign
arr[5] = 'baz'
which results in our array looking like this:
arr
>> ["foo", "bar", "qux", undefined, undefined, "baz"]
And the length is as expected
arr.length
>> 6
JavaScript has kindly expanded our array to the needed length - six - and the new items are set to undefined - except for the one we actually assigned a value to.
From a low level point of view this is horrible memory-wise. Typically an array would be a continuous range in memory - making an array bigger generally involves copying the whole array to a new memory location, sufficient in size. This is a very costly operation.
Now, I do realize that this is likely not what JavaScript engines are doing, as copying around arrays would be crazy expensive and the memory space would be wasted on all these 'undefined' values.
Can someone tell me what actually happens behind the door?
Are arrays actually some sort linked lists?
Are the 'undefined' array items actually there?
How expensive is it to work with large arrays that are mostly filled with 'undefined'?
In the first version of JavaScript, there were no arrays. They were later introduced as a sub-class of that "mother of all objects": Object. You can test this quite easily by doing this:
var foo = [1,2,3,4];
for (var n in foo)
{//check if n is equal (value and type) to itself, coerced to a number
console.log(n === +(n) ? 'Number' : 'String');
}
This will log String, time and time again. Internally, all numeric keys are converted to strings. The Length property merely fetches the highest index, and adds 1 to it. Nothing more. When you display your array, the object is iterated, and for each key, the same rules apply as for any object: first the instance is scanned, then the prototype(s)... so if we alter our code a bit:
var foo = [1,2,3,4];
foo[9] = 5;
for (var n in foo)
{
if (foo.hasOwnProperty(n))
{//check if current key is an array property
console.log(n === +(n) ? 'Number' : 'String');
}
}
You'll notice the array only has 5 own properties, the undefined keys 4-8 are undefined, because there was no corresponding value found within the instance, nor in any of the underlying prototypes. In short: Arrays aren't really arrays, but objects that behave similarly.
As Tim remarked, you can have an array instance with an undefined property that does exist within that object:
var foo = [1,2,undefined,3];
console.log(foo[2] === undefined);//true
console.log(foo[99] === undefined);//true
But again, there is a difference:
console.log((foo.hasOwnProperty('2') && foo[2] === undefined));//true
console.log((foo.hasOwnProperty('99') && foo[99] === undefined));//false
RECAP, your three main questions:
Arrays are objects, that allow you to reference their properties with numeric instances
The undefined values are not there, they're merely the default return value when JS scans an object and the prototypes and can't find what you're looking for: "Sorry, what you ask me is undefined in my book." is what it says.
Working with largely undefined arrays doesn't affect the size of the object itself, but accessing an undefined key might be very, very marginally slower, because the prototypes have to be scanned, too.
Update:
Just quoting the Ecma std:
15.4 Array Objects
Array objects give special treatment to a certain class of property names. A property name P (in the form of a
String value) is an array index if and only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal to
2^32
ď€1. A property whose property name is an array index is also called an element. Every Array object has a
length property whose value is always a nonnegative integer less than 2^32. The value of the length
property is numerically greater than the name of every property whose name is an array index; whenever a
property of an Array object is created or changed, other properties are adjusted as necessary to maintain this
invariant. Specifically, whenever a property is added whose name is an array index, the length property is
changed, if necessary, to be one more than the numeric value of that array index; and whenever the length
property is changed, every property whose name is an array index whose value is not smaller than the new
length is automatically deleted. This constraint applies only to own properties of an Array object and is
unaffected by length or array index properties that may be inherited from its prototypes.
An object, O, is said to be sparse if the following algorithm returns true:
1. Let len be the result of calling the [[Get]] internal method of O with argument "length".
2. For each integer i in the range 0≤i
a. Let elem be the result of calling the [[GetOwnProperty]] internal method of O with argument
ToString(i).
b. If elem is undefined, return true.
3. Return false.
Arrays are just an ordered list of objects. In JavaScript everything is an object, so arrays are not really arrays as we know them :)
You can find little internals here.
For your doubts about working with large arrays... Well, remember that the less calculation you make "client-side", the faster will be your page.
Answers:
An array in JavaScript is just the same as an object (i.e. an unordered collection of properties) with a magic length property and extra prototype methods (push() etc.)
No, the undefined items are not there. JavaScript has an in operator that test for the existence of a property that you can use to prove this. So for the following array: var arr = ['foo']; arr[2] = 'bar';, 2 in arr returns true and 1 in arr returns false.
A sparse array should take up no more memory than a dense array whose length is the number of properties actually defined in your sparse array. It will only be more expensive to work with a sparse array when you iterate over its undefined properties.
Most javascript implementations implement arrays as some flavor of binary tree or hash table with the array index as the key, so a large range of undefined objects does not use up any memory.
I was told that the arrays come in 2 parts, [value, pointer]. So the pointer of arr[2] is null. When you add a 5 it changes the address from null to point to number 3, which points to number 4, which points to number 5, which is null (so end of array).
Im not sure how true this is as ive never actually checked it. But it seems to make sense.
So you cant do the maths like on a c type array (ie to get to value 4 just do starting memory point + 4x (object amount in memory)) but you can do it by following the array peice by peice