Array index as property - javascript

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.

Related

What are "non-existent property values" in ECMA spec for ARRAY:SORT

In an answer for this question (which otherwise I can fully understand/etc), there'a this curious quite:
From the spec, 15.4.4.11 :
Because non-existent property values always compare greater than
undefined property values, and undefined always compares greater than
any other value, undefined property values always sort to the end of
the result, followed by non-existent property values.
I've checked in the latest version available now and it's "note 1" at the end of sort spec, and it's basically the same as it was when that answer from 2011 was written.
Regarding undefined property values always sort to the end of the result, followed by non-existent property values -- how can it be? what are "non-existent property values"(*)? if we write a.foo and a doesn't have such property, we'll get undefined, so how can it be differentiated?
The sort is called either without any parameter, or with a comparer-style function, and in the latter case, it's our function, and we're bound to read the non-existent property and get undefined.. The sort can't inspect the object's keys for us to decide whever an inspected object has a property or not (in contrast to i.e. certain underscore/lodash helpers where you define a 'path' like i.e. pluck or get). I just dont see how we could trigger this "non-existent property values" case at all.
(*) I've found something that looks like a definition of this term here:
A non-existent property is a property that does not exist as an own property on a non-extensible target.
(...)
If the target is non-extensible and P is non-existent, then all future
calls to [[GetOwnProperty]] (P) on the target must describe P as
non-existent (i.e. [[GetOwnProperty]] (P) must return undefined).
This must-describe-as-nonexistent and must-return-undefined seem to support my doubt.
I've also noticed that the pseudo-code for SortIndexedProperties (used to define sort) actually contains bits like 3.b. Let kPresent be ? HasProperty(obj, Pk).. So maybe that non-existent property part in sort spec meant to cover some case like the array being mutated by the comparer function and certain keys are removed from it?
A non-existent property on an array will be "included" when sorting only if the array is sparse. Here's an example, look at the comments:
const arr = []; // 0-length array
arr[2] = 'foo'; // Turns into 3-length array. Does not have own properties 0 or 1
arr.sort();
console.log(arr[0]); // Sort result includes a value from defined property
console.log(arr.hasOwnProperty(1)); // But not from the sparse elements
console.log(arr.length); // But the array will have the same length as originally
As you can see, the properties that didn't exist on the original sparse array are "sorted" to the end of the array, and don't exist on the resulting sorted array either, except for the .length of the sorted array. It started out as an array with a length of 3 and only one own-property, and ended up as an array with a length of 3 and only one own-property (but with that own-property at a different index).
if we write a.foo and a doesn't have such property, we'll get undefined, so how can it be differentiated?
It's equivalent to a .hasOwnProperty check:
const obj1 = {};
const obj2 = { foo: undefined };
console.log(obj1.hasOwnProperty('foo'));
console.log(obj2.hasOwnProperty('foo'));
const arr1 = ['abc'];
const arr2 = [, 'def']; // sparse array
console.log(arr1.hasOwnProperty(0));
console.log(arr2.hasOwnProperty(0));
Best approach to all of this - never use sparse arrays to begin with, they're too confusing.

Do array instance and Array.prototype share a single length property?

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

Javascript Object Storage Order

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; }

In writing Chrome extensions, how is "var i in windows" (within a callback function(windows)) compiled?

The array of Window windows which is passed to the callback has a number of properties, so how does the compiler know what "var i in windows" is referring to?
The code snippet looks like this:
chrome.windows.getAll({"populate":true}, function(windows) {
for (var i in windows) {
var tabs = windows[i].tabs;
In the next line windows[i].tabs windows is being passed as an array, so I understand how that works but not var i in windows.
It might not be the best approach, but it works for Array.
You should read the MDN entry on for...in to understand how it works.
This iterates over enumerable properties of an object.
That explains why things like windows.length do not get into the loop. They are simply defined by the Array to be non-enumerable by the means of .propertyIsEnumerable(), and the same for inherited things:
Objects created from built–in constructors like Array and Object have inherited non–enumerable properties from Object.prototype and String.prototype, such as String's indexOf() method or Object's toString() method.
And about arrays specifically:
Array indexes are just enumerable properties with integer names and are otherwise identical to general Object properties. There is no guarantee that for...in will return the indexes in any particular order and it will return all enumerable properties, including those with non–integer names and those that are inherited.
Further reading:
Object.prototype.propertyIsEnumerable()
Enumerability and ownership of properties
Note that documentation recommends using a standard for loop, a for...of loop or .forEach() in case of arrays.
I'm not sure how the properties of your parameter windows are defined, but if you don't get them on the for(var i in windows) it means that they aren't defined as usual properties like this :
windows.prop = "value";
windows['prop'] = "value";
One way of defining a propety which you can't find using your loop is using the Object.defineProperty , check this example :
Object.defineProperty( windows, 'prop', {
get : function () {
return 'value';
}
});
Now when you use a for(var i in windows) loop, you won't get the property prop
In JavaScript you can access properties of an object using either the dot notation or the squared brackets notation.
var a = {
b = 'c'
};
a.b === a['b']; // true
So in the snippet you're showing, the for loop loops over all indices of the array (in fact it will also loop over all properties on the array, so I don't understand why a for(var i = 0; ...) solution isn't used here). And then it simply accesses the elements of the array by requesting the property with the squared brackets notation.
AFAICT an alternative notation of the snippet you're showing would be:
for(var i = 0; i < windows.length; i++){
var tabs = windows[i].tabs;
}
Assuming windows is indeed an array.

Can I assume the order of response from Object.keys same?

{"/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...

Categories

Resources