In python, a list, even when it is a class, prohibits its instances from creating more user attributes.
>> x = list()
>> x.new_attribute = 90
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'new_attribute'
This as expected throws error even when classes could possibly accept more attributes through __dict__. This makes sense.
Even though it isn't exactly similar, my questions is that javascript arrays act weirdly when they are given properties.
let x = [1,2,3]
x.foo = "this is unexcpected"
console.log(x)
This would print [1, 2, 3, foo: "this is unexcpected"]. Here is my questions, Aren't arrays supposed to have all their keys to be autogenerated numbers? I mean if I check the length it is still three and it isn't counting foo as part of the array in the usual sense, more over all the methods like pop doesn't apply to the key-value pair. This makes a good sense, but makes me wounder if this (addition of key-value pairs) is allowed so that they could be used to store metadatas about the arrays, after all length is also kind of a property. One more questions, if arrays can store key-value pairs, what makes them different from object except some of the methods like inserting, poping ...?
I know this thread is old, but I find the question interesting...
The main part of the question is what's the difference between an object and an array. As mentioned in the comments, Array.prototype IS an object. Which allows us to do some kind of weird things. Take this code for example:
let y = [0,1,2,3];
y.foo = "Testing";
console.log(y);
y.sample = function(){
console.log("Hello world");
}
y.sample()
Yes, you can add methods to an array, on the console it outputs this:
> (4) [0, 1, 2, 3, foo: 'Testing']
0: 0
1: 1
2: 2
3: 3
foo: "Testing"
sample: ƒ ()
length: 4
[[Prototype]]: Array(0)
> Hello World
Considering the way that Javascript was made, we can do odd things like add methods to arrays and treat them as objects. However, these objects have intended uses and built-in methods provided by the developers (push, pop, etc.). Maybe you could create your own modified datatype in JS by modifying the Array.prototype object. Outside of that, it feels kinda weird to add methods and properties to arrays...at that point just make an object for the sake of readability, but hey, as long as your code is understandable and it works, it's good to me.
Related
This is a question about Javascript(and possibly other languages) fundamentals.
I was developing a project and at some point a realized I had defined the same key more than once in an object but no error appeared on console. After some research I couldn't find a clear/official statement to what happens in this situation or how this affects the program. All I could find out is the output in a simple example:
let obj = {key1:1, key1:2, key1:3};
console.log(obj.key1);// >> output = 3
console.log(Object.keys(obj));// >> output = ['key1']
My question is: The value is just redefined and all previous declarations are erased? Is this a limitation or some kind of error of Javascript?
The value is just redefined and all previous declarations are erased?
Yes. When an object literal contains duplicate keys, only the final key in the object literal will exist on the object after the object is evaluated.
Is this a limitation or some kind of error of Javascript?
It's permitted, but it's nonsense. Like lots of things in programming, there are things which are syntactically allowed but don't make any sense in code.
But you can use a linter to prevent from making these sorts of mistakes, eg ESLint's no-dupe-keys.
There's one case where something very similar to duplicate keys can be common, which is when using object spread, eg:
const obj = { foo: 'foo', bar: 'bar' };
// Now say we want to create a new object containing the properties of `obj`
// plus an updated value for `foo`:
const newObj = { ...obj, foo: 'newFoo' }
This sort of approach is very common when working with data structures immutably, like in React. It's not exactly the same thing as an outright duplicate key in the source code, but it's interpreted the same way - whatever value gets interpreted last in the key-value list (regardless of if the key is spread or static) will be the value that the final object contains at that key.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 3 years ago.
Improve this question
I'm looking for an answer other than "bad practice".
PLEASE, this is a question about TERMINOLOGY only! This is NOT a question about alternative ways this could be done, why it's better to use an object or Map instead of an array, why the length property of the array and the number of keys are different, etc.
The terminology "associative array" seems to typically be used for an object declared with curly braces, but then accessed with square brackets, like this:
const a = {'not-a-proper-identifier': 'foo'};
a['not-a-proper-identifier'] // returns 'foo'
But you can do this:
const a = [1, 2, 3];
a['foo'] = 'bar';
a['foo']; // Yep, 'bar' comes back.
console.log(a); // Output (at least from Chrome) is [1, 2, 3, foo: "bar"]
Array.isArray(a); // This is still true
a.length; // 3
Object.keys(a).length; // 4
JSON.stringify(a); // Only returns [1,2,3], 'foo' disappears.
[1, 2, 3, foo: "bar"] // Syntax error! The console representation isn't proper JavaScript.
So, if you were to do this with an array, is there something special to call this kind of array?
Why do I ask?
NOT because I particularly want to use this data structure myself, but because I would like to be able to support serializing and deserializing such an array, if and when encountered, for a variation on JSON that I'm working on, JSON-Z, a fork off the JSON5 project.
And if I'm going to support it, it would be nice to know what terminology use when referring to an array like this.
Let's take it as written that adding non-numeric keys to an array is not a good idea; the comments clearly agree on this and it's evident enough from the code example you've provided. The language struggles, on the surface, to handle this consistently.
The explanation of the code you've written is probably not necessary but I feel it's good to illustrate as clearly as possible why some of the surface inconsistencies exist in javascript so as to better understand how the language works.
Examining the example you provide:
const a = [1, 2, 3];
a is an array, but arrays are objects in javascript.
a['foo'] = 'bar';
You can set properties on objects.
a['foo'];
This will indeed be bar; we're just accessing a property on an object
console.log(a); // Output (at least from Chrome) is [1, 2, 3, foo: "bar"]
Chrome seems to have done an admirable job at interpreting how to log this to a console, it could alternatively just log this out as a standard object.
Array.isArray(a); // This is still true
It is true; Array.isArray checks the Class internal property in order to determine what is an array. This answer indicates that this can't be changed.
a.length; // 3
Length is calculated as the max numeric key + 1
JSON.stringify(a); // Only returns [1,2,3], 'foo' disappears.
Indeed - JSON cannot handle this kind of mixing of keys (as you probably know, considering you're working on a project to extend JSON) so it has to decide whether to parse the object as a json object, or a json array.
The console representation is really only as good a representation of the data being logged as is possible, and given the mixed nature of javascript objects - used for both arrays and dictionaries, effectively, it's hard to get a perfect representation of the data.
As to your actual question... I haven't been able to find a name for those object properties. I'd say the best description I could imagine is:
Object properties which do not conform to standard javascript naming
syntax
This isn't particularly good but it seems that the actual answer to your question is simply no, there isn't a name for these.
It's just "object". obj['foo'] is an equivalent of obj.foo.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
I have generally found javascript to be transparent, in that there are very few black boxes where "magic" just happens and you should just accept and look the other way, however I have not found any answer to how the Array brackets [] notation actually works under the hood.
let arr = [4, 5, 6, 7]
console.log(arr[3]) // <- How does this work?
What is javascript doing to access the item at index 3. Does it internally call some method on the Array.prototype?
With an object, the [] is a shortcut for a property accessor.
let obj = {
a: 'hello',
b: 'world'
}
obj['a'] === obj.a // true
Is an array then just an object with a long list of integer based properties?
let objArray = {
0: 'hello',
1: 'world'
}
let realArray = ['hello', 'world']
objArray[0] === 'hello' // true
realArray[0] === 'hello' // true
objArray.0 // SyntaxError: Unexpected number
realArray.0 // SyntaxError: Unexpected number
I have seen many many online discussions that all come to the conclusion that you cannot overload the brackets notation to truly subclass an Array but I have never seen an explanation on what magic is happening under the hood that allows the Array to work the way it does.
The obvious follow up question is whether there is any way to intercept the bracket notation access to define your own behavior, but I think I already know the answer to that.
You'd probably have to look at the implementation code to know precisely what's going on, but the basic idea is that arrays are actually layered atop objects.
This is backwards:
With an object, the [] is a shortcut for a property accessor.
The bracket notation is more fundamental. Thus, obj['foo'] and obj.foo work the same, but there is no equivalent for obj['foo & bar'], which is perfectly legitimate, and will respond with a value if obj has a key named "foo & bar".
Is an array then just an object with a long list of integer based properties?
Not quite, but you're not far off. Arrays are objects with the Array prototype, and with a little bit of additional magic to set the length property when new keys are added, or remove keys when that length is set.
And no, you cannot override the [] operator for your own purposes.
Is an array then just an object with a long list of integer based properties?
Yes, in it's simplest form, an Array is an Object with a list of integer base properties that is based on the Array prototype (which gives access to all the array methods like map, forEach, etc.)
As for intercepting the bracket notation, no, I have not seen anything that would allow that besides creating your own Object that has the methods you need (and then only access that object via the appropriate methods).
More info from MDN:
Arrays are list-like objects whose prototype has methods to perform traversal and mutation operations. Neither the length of a JavaScript array nor the types of its elements are fixed. Since an array's length can change at any time, and data can be stored at non-contiguous locations in the array, JavaScript arrays are not guaranteed to be dense; this depends on how the programmer chooses to use them. In general, these are convenient characteristics; but if these features are not desirable for your particular use, you might consider using typed arrays.
Arrays cannot use strings as element indexes (as in an associative array) but must use integers. Setting or accessing via non-integers using bracket notation (or dot notation) will not set or retrieve an element from the array list itself, but will set or access a variable associated with that array's object property collection. The array's object properties and list of array elements are separate, and the array's traversal and mutation operations cannot be applied to these named properties.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Indexed_collections#Array_object
When I access arguments from inside called function in nodejs...
echo '!function(){ console.log(arguments) }(1,2,3)' | node
... it outputs an object ...
{ '0': 1, '1': 2, '2': 3 }
... but in google chrome ...
!function(){ console.log(arguments) }(1,2,3)
... outputs ...
[1, 2, 3]
Why is it not consistent? What other js environments have this different behaviour? Is it ok for me to use arguments? How can I ensure it is always an array before I use it?
There are no rules for console output. The output doesn't represent anything that has to do with the language standard.
If you create a program that logs values from within a JS program, you're free to display those values however you want.
The arguments object is the same in both places. Only how it's being displayed is different.
How can I ensure it is always an array before I use it?
It's never an array. It's always an array-like object. In both cases its showing a value at object members 0, 1 and 2. Just uses different display syntax to do it.
In Chrome, try creating an object like this:
var my_obj = {
"0": 1,
"2": 3,
"1": 2,
length: 3,
splice: function(){},
};
And the log it in the console. It'll probably look like an Array literal, though it's obviously not.
It's not an Array in the browser, either. The arguments object has actually been in JavaScript longer than Arrays have, and when Arrays were first added to JavaScript (back in its 1.1 days), they didn't update arguments to be an Array. They haven't done it since then either, because of worries about backward-compatibility. It shows up in some browser consoles as though it were an Array because the debug console knows that most people plan to treat it as one anyway.
But the arguments object is close enough to an Array that it's easy to convert. One way to do it is like this:
var args = Array.prototype.slice.call(arguments, 0);
Array.prototype.slice, like most of Array.prototype's methods, is written to be generic: you can call it on Objects that aren't Arrays, and as long as they have properties with numeric names and a length property that's a Number, it'll work. The arguments object has both of these things, so these functions will work on it.
The slice function returns an Array which is a shallow copy of whatever object was passed into it, starting and ending at whatever numbers you specify. We tell it to start at zero, and because we didn't say where to stop, it assumes we want it to stop at the end, wherever that might be. This way, we copy all of the elements of arguments into a new Array, and now you can do whatever other Array things you want with it.
Namely, how does the following code:
var sup = new Array(5);
sup[0] = 'z3ero';
sup[1] = 'o3ne';
sup[4] = 'f3our';
document.write(sup.length + "<br />");
output '5' for the length, when all you've done is set various elements?
My 'problem' with this code is that I don't understand how length changes without calling a getLength() or a setLength() method. When I do any of the following:
a.length
a['length']
a.length = 4
a['length'] = 5
on a non-array object, it behaves like a dict / associative array. When I do this on the array object, it has special meaning. What mechanism in JavaScript allows this to happen? Does JavaScript have some type of property system which translates
a.length
a['length']
into "get" methods and
a.length = 4
a['length'] = 5
into "set" methods?
Everything in JavaScript is an object. In the case of an Array, the length property returns the size of the internal storage area for indexed items of the array. Some of the confusion may come into play in that the [] operator works for both numeric and string arguments. For an array, if you use it with a numeric index, it returns/sets the expected indexed item. If you use it with a string, it returns/sets the named property on the array object - unless the string corresponds to a numeric value, then it returns the indexed item. This is because in JavaScript array indexes are coerced to strings by an implicit toString() call. Frankly, this is just one more of those things that makes you scratch your head and say "JavaScript, this, this is why they laugh at you."
The actual underlying representation may differ between browsers (or it may not). I wouldn't rely on anything other than the interface that is supplied when working with it.
You can find out more about JavaScript arrays at MDN.
Characteristics of a JavaScript array
Dynamic - Arrays in JavaScript can grow dynamically .push
Can be sparse - for example, array[50000] = 2;
Can be dense - for example, array = [1, 2, 3, 4, 5]
In JavaScript, it is hard for the runtime to know whether the array is going to be dense or sparse. So all it can do is take a guess. All implementations use a heuristic to determine if the array is dense or sparse.
For example, code in point 2 above, can indicate to the JavaScript runtime that this is likely a sparse array implementation. If the array is initialised with an initial count, this could indicate that this is likely a dense array.
When the runtime detects that the array is sparse, it is implemented in a similar way to an object. So instead of maintaining a contiguous array, a key/value map is built.
For more references, see How are JavaScript arrays implemented internally?
This really depends on what you intend to do with it.
[].length is "magical".
It doesn't actually return the number of items in the array. It returns the largest instated index in the array.
var testArr = []; testArr[5000] = "something"; testArr.length; // 5001
But the method behind the setter is hidden in the engine itself.
Some engines in some browsers will give you access to their implementations of those magic-methods.
Others will keep everything completely locked down.
So don't rely on defineGetter and defineSetter methods, or even, really, __proto__ methods, unless you know which browsers you know you're targeting, and which you aren't.
This will change in the future, where opt-in applications written in ECMAScript Next/6 will have access to more.
ECMAScript 5-compliant browsers are already starting to offer get and set magic methods in objects and there's more to come... ...but it's probably a while away before you can dump support for oldIE and a tonne of smartphones, et cetera...
It is important to know that when you do sup['look'] = 4; you are not using an associative array, but rather modify properties on the object sup.
It is equivalent to sup.look = 4; since you can dynamically add properties on JavaScript objects at any time. sup['length'] would for an instance output 5 in your first example.
To add to tvanfosson's answer: In ECMA-262 (the 3.0 specification, I believe), arrays are simply defined as having this behavior for setting properties (See 15.4.5.1). There's no general mechanism underlying it (at least as of now) - this is just how it's defined, and how JavaScript interpreters must behave.
As other people have mentioned, a property in JavaScript can basically act as both as getter and a setter of your array (or string or other inputs).
As a matter of fact, you might try this yourself:
const test = [1, 2, 3, 4, 5]
test.length = 3
console.log(test) // [1, 2, 3]
test.length = 5
console.log(test) // Guess what happens here!
As far as I know, arrays in JavaScript do not work exactly like associative arrays and you have elements which are put in memory as contiguously as possible (given that you can have arrays of mixed objects), depending on the JavaScript engine you are considering.
As a side note, I am a bit baffled that the most voted answer keeps spreading the over-simplified myth (or half-truth) of "everything being an object in JavaScript"; that is not exactly true, otherwise you will never study primitives, for example.
Try to do this:
const pippi = "pippi"
pippi.cat = "cat"
console.log(pippi.cat) // Will it work? Throw an error? Guess why again
Spoiler: the string is wrapped in a throwaway object for that specific operation on the second line, and then in the following one you are just going to access a property of the primitive which is not there (provided you did not play with String.prototype or the like), so you get undefined.
Array object inherits caller, constructor, length, and name properties from Function.prototype.
A JavaScript array is an object just like any other object, but JavaScript gives it special syntax.
arr[5] = "yo"
The above is syntactic sugar for
arr.insert(5,"yo")
which is how you would add stuff to a regular object. It's what is inside the insert method that changes the value of arr.length
See my implementation of a customArray type here: http://jsfiddle.net/vfm3vkxy/4/