Crockford writes in http://javascript.crockford.com/survey.html:
"There are two ways to make a new array:
var myArray = [];
var myArray = new Array();"
So I'm confused by these two lines in some AJAX code I am reading:
var obj={}; // obj is an Object object (i.e. a hash table)
obj[4] = 'x'; // now obj is suddenly an Array object via an integer key?
In JavaScript are an object and an array really just the same thing, but with a variant on the key type?
In other words, is this the same as in php where we can use either a name (string) or an integer for a hash key?
I've Googled for an answer on this but can't seem to nail down an article which discusses this issue.
One possibility that comes to mind is that perhaps the first line is syntactic lint because the 2nd line overwrites the previous definition of obj as it creates a new Array object.
it does not become an array, it is simply an Object with a '4' property, like this:
var obj = {
'4': 'x'
};
it is just converted to a string when used as a property like obj['4'] = 'x';
Everything but primitive datatypes is an object in JavaScript. Objects can have a properties and there are two ways to access object properties:
Dot notation, foo.bar, which you can use as long as the property name is a valid identifier.
Bracket notation, foo['bar'] which you have to use if the key is not a valid identifier [spec]. For example, if it is a number, or contains a space or you have a variable with the name.
Hence, bracket notation is not a characteristic of arrays and if you see it, it does not mean the value is an array. It is simple one of two ways of accessing properties.
The elements of an array are just properties with numeric keys. Arrays are built on top of objects and implement some additional methods which treat these numeric properties in a special way. For example the .length property is automatically updated when you add new elements. But ultimately they are just normal properties.
In your example you have a simple object. You have to access the property with obj[4] or obj['4'] because obj.4 is invalid since 4 is not a valid identifier (basically everything that you can use as variable name is a valid identifier. var 4 = 'foo'; is invalid).
And since arrays are just objects, if you could use numbers as identifiers, you were also able to access an element with arr.4.
As far as I know, no, an object can't be coerced into an array. But, it can look and act like an array, and that's what's happening here. Numbers, and anything else that can be coerced to a string, are perfectly valid property names for Javascript objects, so
obj[4] = 1;
obj['spam'] = 2;
are both valid ways of setting a property on the object. That doesn't make the object an array. An Array is a special class of object with specific methods (.slice(), .concat(), etc) and a length property that's kept up to date with the number of items in the array.
Yes
Javascript Array is very different from tradition array, you can think of it as object.
var array = [1,2,3] is equivalent to var object = {'0' : 1, '1' : 2, '2' : 3}
except array inherited from Array.prototype and object inherited from Object.prototype, where Array.prototype will contain method such as length.
Javascript is a loosely-typed, prototype-based language. Even primitive types like a boolean can be treated like an object (though you aren't going to get far). Almost everything in javascript is, at root, an object.
Understanding this, an array IS an object. You can arbitrarily add properties to any object:
var xml = new XMLHttpRequest();
xml[4] = 'x';
console.log(xml);
That object is still an instance of XMLHttpRequest. It now has a property labeled 4 with a value of x. You can treat anything like this -- even a function:
var test_func = function () {
alert('woah!');
}
test_func[4] = 'x';
console.log(test_func[4]);
The take-away here is that the obj[key] = value notation is NOT indicative of an "array" type, like it is in languages such as PHP. Rather, it is an alternate way to access properties of any object, and is equivalent to obj.key = value (you can't use obj.4 = 'x', though, that's invalid syntax). The other take-away is that any object in javascript can be modified or used in pretty much any way. You shouldn't misuse objects, but you can
Check it out here: http://jsfiddle.net/w2AqJ/
Documentation
Array on MDN - https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array
Javascript "associative arrays" considered harmful by Andrew Dupont - http://andrewdupont.net/2006/05/18/javascript-associative-arrays-considered-harmful/
Related
This question already has answers here:
Add a property to a JavaScript object using a variable as the name? [duplicate]
(14 answers)
Closed 8 years ago.
I'm trying something like this, but this example does not work.
jsObj = {};
for (var i = 1; i <= 10; i++) {
jsObj{'key' + i} = 'example ' + 1;
}
What can I do to make a dynamic key like this?
Square brackets:
jsObj['key' + i] = 'example' + 1;
In JavaScript, all arrays are objects, but not all objects are arrays. The primary difference (and one that's pretty hard to mimic with straight JavaScript and plain objects) is that array instances maintain the length property so that it reflects one plus the numeric value of the property whose name is numeric and whose value, when converted to a number, is the largest of all such properties. That sounds really weird, but it just means that given an array instance, the properties with names like "0", "5", "207", and so on, are all treated specially in that their existence determines the value of length. And, on top of that, the value of length can be set to remove such properties. Setting the length of an array to 0 effectively removes all properties whose names look like whole numbers.
OK, so that's what makes an array special. All of that, however, has nothing at all to do with how the JavaScript [ ] operator works. That operator is an object property access mechanism which works on any object. It's important to note in that regard that numeric array property names are not special as far as simple property access goes. They're just strings that happen to look like numbers, but JavaScript object property names can be any sort of string you like.
Thus, the way the [ ] operator works in a for loop iterating through an array:
for (var i = 0; i < myArray.length; ++i) {
var value = myArray[i]; // property access
// ...
}
is really no different from the way [ ] works when accessing a property whose name is some computed string:
var value = jsObj["key" + i];
The [ ] operator there is doing precisely the same thing in both instances. The fact that in one case the object involved happens to be an array is unimportant, in other words.
When setting property values using [ ], the story is the same except for the special behavior around maintaining the length property. If you set a property with a numeric key on an array instance:
myArray[200] = 5;
then (assuming that "200" is the biggest numeric property name) the length property will be updated to 201 as a side-effect of the property assignment. If the same thing is done to a plain object, however:
myObj[200] = 5;
there's no such side-effect. The property called "200" of both the array and the object will be set to the value 5 in otherwise the exact same way.
One might think that because that length behavior is kind-of handy, you might as well make all objects instances of the Array constructor instead of plain objects. There's nothing directly wrong about that (though it can be confusing, especially for people familiar with some other languages, for some properties to be included in the length but not others). However, if you're working with JSON serialization (a fairly common thing), understand that array instances are serialized to JSON in a way that only involves the numerically-named properties. Other properties added to the array will never appear in the serialized JSON form. So for example:
var obj = [];
obj[0] = "hello world";
obj["something"] = 5000;
var objJSON = JSON.stringify(obj);
the value of "objJSON" will be a string containing just ["hello world"]; the "something" property will be lost.
ES2015:
If you're able to use ES6 JavaScript features, you can use Computed Property Names to handle this very easily:
var key = 'DYNAMIC_KEY',
obj = {
[key]: 'ES6!'
};
console.log(obj);
// > { 'DYNAMIC_KEY': 'ES6!' }
Associative Arrays in JavaScript don't really work the same as they do in other languages. for each statements are complicated (because they enumerate inherited prototype properties). You could declare properties on an object/associative array as Pointy mentioned, but really for this sort of thing you should use an array with the push method:
jsArr = [];
for (var i = 1; i <= 10; i++) {
jsArr.push('example ' + 1);
}
Just don't forget that indexed arrays are zero-based so the first element will be jsArr[0], not jsArr[1].
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
As I've tested, $('foo') is an instance of jQuery, and not an instance of Array. And AFAIK, javascript doesn't have operator overloading.
So how can $('foo')[0] return the corresponding HTMLElement if $('foo') is not an instance of Array?
I tried to look at jQuery's source code but it's a bit too much for me right now.
Thanks for any insight.
Because you can use brackets notation with any object in JavaScript, not just arrays. In fact, normal arrays aren't really arrays at all in JavaScript, they're just objects with a specific prototype and special treatment of a class of property names ("array indexes") and a special length property.
Brackets notation can be used to access any object property1 via its string name (and believe it or not, [0] is converted to ["0"] when accessing arrays, in theory).
Example:
var obj = {foo: "bar", 0: "zero"};
console.log(obj[0]); // zero
console.log(obj["0"]); // zero
console.log(obj.foo); // bar
console.log(obj["foo"]); // bar
var f = "f" + "o" + "o";
console.log(obj[f]); // bar
The object above has two properties of its own. Their names are "0" and "foo"; both names are strings (even though I wrote 0 as a numeric literal in the initializer; it gets coerced to string).
jQuery maintains its "array entry" properties to ensure that you can use brackets notation to access them. It also maintains a length property.
If jQuery were being written from scratch today, it probably would extend Array. But when jQuery was written, you couldn't extend Array. (It only became possible as of ES2015's ("ES6's") class feature.)
1 Except properties that don't have a string name; ES2015 introduced the concept of properties with names that are Symbols rather than strings. Prior to that, all object property names were strings (even array indexes).
Are there any pitfalls to code like this?
var Foo = function() {
this.bar = function() { return 'bar'; };
};
var f = new Foo();
f[0] = 'hi';
f[1] = 'there';
Note that I'm creating a new function object with some misc properties, and then I'm treating the object like an array. Also how are the array values being stored in the object? Are 0 and 1 treated like property names?
Well, yes, 0, and 1 will be just two property names.
When you assign a property with the bracket notation, the expression between the brackets will be converted to String, and that string will be used as the property name.
In fact, even the indexes for real arrays are just that, properties:
var realArray = ['a'];
realArray.hasOwnProperty('0'); // true
The difference is that real array objects on every property assignment that correspond to a valid index[1], track internally the value of their length property.
That's one of the reasons why "subclassing" array objects is difficult, even with the new ECMAScript 5 extensions, or at the moment also with the proposed ECMAScript-Harmony Proxies 2 3, can't be completely done, in a stanard way.
That can be a problem, depending on how you plan to iterate the numeric properties.
If you enumerate the properties with the for-in statement, other members will appear, not just the numeric properties.
[1] A valid array index is any unsigned 32-bit integer in the range of 0 to (2^32)-1.
I think you might have problems if you try to loop through that with a for in loop; the loop will also get bar. There are ways around this, so you just have to be careful.
If you want to extend Array behavious please use : Array.prototype.yourfunc = function()....
This question already has answers here:
Add a property to a JavaScript object using a variable as the name? [duplicate]
(14 answers)
Closed 8 years ago.
I'm trying something like this, but this example does not work.
jsObj = {};
for (var i = 1; i <= 10; i++) {
jsObj{'key' + i} = 'example ' + 1;
}
What can I do to make a dynamic key like this?
Square brackets:
jsObj['key' + i] = 'example' + 1;
In JavaScript, all arrays are objects, but not all objects are arrays. The primary difference (and one that's pretty hard to mimic with straight JavaScript and plain objects) is that array instances maintain the length property so that it reflects one plus the numeric value of the property whose name is numeric and whose value, when converted to a number, is the largest of all such properties. That sounds really weird, but it just means that given an array instance, the properties with names like "0", "5", "207", and so on, are all treated specially in that their existence determines the value of length. And, on top of that, the value of length can be set to remove such properties. Setting the length of an array to 0 effectively removes all properties whose names look like whole numbers.
OK, so that's what makes an array special. All of that, however, has nothing at all to do with how the JavaScript [ ] operator works. That operator is an object property access mechanism which works on any object. It's important to note in that regard that numeric array property names are not special as far as simple property access goes. They're just strings that happen to look like numbers, but JavaScript object property names can be any sort of string you like.
Thus, the way the [ ] operator works in a for loop iterating through an array:
for (var i = 0; i < myArray.length; ++i) {
var value = myArray[i]; // property access
// ...
}
is really no different from the way [ ] works when accessing a property whose name is some computed string:
var value = jsObj["key" + i];
The [ ] operator there is doing precisely the same thing in both instances. The fact that in one case the object involved happens to be an array is unimportant, in other words.
When setting property values using [ ], the story is the same except for the special behavior around maintaining the length property. If you set a property with a numeric key on an array instance:
myArray[200] = 5;
then (assuming that "200" is the biggest numeric property name) the length property will be updated to 201 as a side-effect of the property assignment. If the same thing is done to a plain object, however:
myObj[200] = 5;
there's no such side-effect. The property called "200" of both the array and the object will be set to the value 5 in otherwise the exact same way.
One might think that because that length behavior is kind-of handy, you might as well make all objects instances of the Array constructor instead of plain objects. There's nothing directly wrong about that (though it can be confusing, especially for people familiar with some other languages, for some properties to be included in the length but not others). However, if you're working with JSON serialization (a fairly common thing), understand that array instances are serialized to JSON in a way that only involves the numerically-named properties. Other properties added to the array will never appear in the serialized JSON form. So for example:
var obj = [];
obj[0] = "hello world";
obj["something"] = 5000;
var objJSON = JSON.stringify(obj);
the value of "objJSON" will be a string containing just ["hello world"]; the "something" property will be lost.
ES2015:
If you're able to use ES6 JavaScript features, you can use Computed Property Names to handle this very easily:
var key = 'DYNAMIC_KEY',
obj = {
[key]: 'ES6!'
};
console.log(obj);
// > { 'DYNAMIC_KEY': 'ES6!' }
Associative Arrays in JavaScript don't really work the same as they do in other languages. for each statements are complicated (because they enumerate inherited prototype properties). You could declare properties on an object/associative array as Pointy mentioned, but really for this sort of thing you should use an array with the push method:
jsArr = [];
for (var i = 1; i <= 10; i++) {
jsArr.push('example ' + 1);
}
Just don't forget that indexed arrays are zero-based so the first element will be jsArr[0], not jsArr[1].