Any programming language which provides arrays (lists, vectors, tuples etc.) must decide whether they have reference or value semantics, with the usual/obvious choice being reference semantics for mutable arrays and value semantics for immutable ones.
JavaScript which provides mutable arrays appears to have chosen reference semantics e.g. given
var a = [1, 2, 3]
var b = [1, 2, 3]
then a != b, as expected because though they have the same contents, they are different arrays.
However when you use them as keys in an object, the picture changes; if you set obj[a] to a value, then obj[b] gets the same value. Furthermore, this remains true if you change the contents of the arrays; at least when I tested it in Rhino, it behaves as though the interpreter were recursively comparing the full contents of the supplied and stored key arrays on every lookup, complete with a check for the infinite loop that would occur if one of the arrays were made to point to itself.
Is this the intended/specified behavior in all implementations?
Does it also apply to objects used as keys?
Is there any way to get the other behavior, i.e. to look up values using arrays as keys with reference semantics?
When arrays are used as property names they are cast to a string:
[1,2,3].toString() == '1,2,3'
Once turned into a string value, arrays with the same contents would map to the same property.
To answer your last question, you can't use objects to reference property names (keys) whereby only the same object maps to the same property (1:1 mapping).
obj[a] and obj[b] will run the toString function on the arrays and produce the same result for both. It doesn't try to use the arrays as keys.
var a = [1,2,3];
var x = {};
x[a] = "test";
var i;
for(i in x)
{
alert(i); //"1,2,3"
}
jsFiddle example
Related
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
In JavaScript, you can have objects, like this:
var a = { foo: 12, bar: 34 };
Or arrays with key (named) indexes, like this:
var b = [];
b['foo'] = 56;
b['bar'] = 78;
They're somewhat similar, but obviously not the same.
Now the strange thing is, JSON.stringify doesn't seem to take the array. No errors or anything, JSON.stringify(b) just results in [].
See this jsfiddle example. Am I doing something wrong, or do I just misunderstand how arrays work?
Javascript doesn't support Associative arrays (Like PHP).
var b = []; Declaring explicitly an array, when you are trying to create an Object.
Arrays in Javascript can only contain the Index approach of Arrays, while Objects are more of
Associative arrays.
If you change var b = []; to var b = {}; it will solve the problem.
var b = {} Declaring explicitly an Object.
Javascript arrays are objects. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Predefined_Core_Objects#Array_Object for details.
Note: if you supply a non-integer value to the array operator in the
code above, a property will be created in the object representing the
array, instead of an array element.
JSON supports only a subset of Javascript. See http://www.json.org/ for details.
JSON is built on two structures:
A collection of name/value pairs. In various languages, this is realized as an object, record, struct, dictionary, hash table, keyed
list, or associative array.
An ordered list of values. In most languages, this is realized as an array, vector, list, or sequence.
A Javascript array that has properties created in the underlying object does not fit into either of these structures because it has both a collection of name/value pairs and an ordered list of values, so there is no simple way to represent such an object directly in JSON.
The JSON.stringify method is defined in the ECMAScript specification. For example, see http://www.ecma-international.org/ecma-262/5.1/#sec-15.12.3.
While there are many details, the bit that is relevant here is how object values are stringified:
If Type(value) is Object, and IsCallable(value) is false
If the [[Class]] internal property of value is "Array" then Return the result of calling the abstract operation JA with argument value.
Else, return the result of calling the abstract operation JO with argument value.
Given your array, despite the addition of parameters to the underlying object, the result is of stringifying the ordered set of array elements, not the underlying object.
There is nothing wrong about adding parameters to an array object, but they are not part of the array and functions or methods that handle arrays might ignore them or deal with them arbitrarily. You have seen that JSON.stringify ignores the additional parameters. Other functions might do otherwise - you will have to find out in each case.
While it is not wrong, it will probably be easier to understand if you do not add properties to array objects. If you want to add properties, start with a non-array object.
Rather than:
var b = [];
b['foo'] = 56;
b['bar'] = 78;
You might use:
var b = {};
b['foo'] = 56;
b['bar'] = 78;
This snap is from IE explorer. See the array is still blank.
Actually the way of inserting the elements to the array is :
1. Use push()
2. insert the elements in the array during declaration
If you want to stringify the array you have to have the data inside the array.
So, now you want to stringify the key value pairs so you have to pass the object as the argument of JSON.stringify() as follows:
var test = {}; // Object
test['a'] = 'test';
test['b'] = []; // Array
test['b'].push('item');
test['b'].push('item2');
test['b'].push('item3');
var json = JSON.stringify(test);
alert(json);
Solution to your problem now:
Note: Console of Google Chrome is giving different result, which is a bug in Google Chrome.
I saw this for the first time (or noticed it for the first time) today during a maintenance of a colleagues code.
Essentially the "weird" part is the same as if you try to run this code:
var arr = [];
arr[0] = "cat"; // this adds to the array
arr[1] = "mouse"; // this adds to the array
arr.length; // returns 2
arr["favoriteFood"] = "pizza"; // this DOES NOT add to the array. Setting a string parameter adds to the underlying object
arr.length; // returns 2, not 3
Got this example from nfiredly.com
I don't know what the technical term for this "case" is so I haven't been able to find any additional information about it here or on Google but it strikes me very peculiar that this "behaviour" can at all exists in JavaScript; a kind of "mix" between Arrays and Objects (or Associative Arrays).
It states in the above code snippet that that Setting a string parameter adds to the underlying object and thus not affect the length of the "array" itself.
What is this kind of pattern?
Why is it at all possible? Is it a weird JS quirk or is it deliberate?
For what purpose would you "mix" these types?
It's possible because arrays are objects with some special behaviors, but objects nevertheless.
15.4 Array Objects
However, if you start using non-array properties on an array, some implementations may change the underlying data structure to a hash. Then array operations might be slower.
In JavaScript, arrays, functions and objects are all objects. Arrays are objects created with Array constructor function.
E.g.
var a = new Array();
Or, using shortcut array literal,
var a = [];
Both are the same. They both create objects. However, special kind of object. It has a length property and numeric properties with corresponding values which are the array elements.
This object (array) has methods like push, pop etc. which you can use to manipulate the object.
When you add a non-numeric property to this array object, you do not affect its length. But, you do add a new property to the object.
So, if you have
var a = [1];
a.x = 'y';
console.log(Object.keys(a)); // outputs ["0", "x"]
console.log(a); // outputs [1];
The following code is intended to remove the lowest number from an array of integers (numbers). I can't for the life of me understand why it is returning an array that is effected by the .sort().reverse().pop();.
I would have thought that assigning var numbersTest to numbers would mean that numbers itself would be unaffected by the aforementioned methods.
Can anyone please shed some light of this javaScript functionality?
function removeSmallest(numbers) {
var numbersTest = numbers;
var lowestNum = numbersTest.sort().reverse().pop();
for (var i = 0; i < numbers.length; i++) {
if (numbers[i] === lowestNum) {
numbers.splice(i, 1);
}
}
return numbers;
}
removeSmallest([1,2,3,4,5]);
This function returns [5, 4, 3, 2]
I want [2,3,4,5]
There are two ways of passing variables in javascript - by value or by reference.
In few words:
Only simple data types passed by value (such as numbers or strings), other types (arrays, objects, ...) are passing by reference. And if you have got many references to one object, performing changes on one of them will affect all (because there are just references). In first approximation you may think of it like symbolic links in unix or shortcuts in windows, when affecting symlink affects the file itself.
See more information here:
Javascript by reference vs. by value
http://snook.ca/archives/javascript/javascript_pass
When you say...
var anArray = [1, 2, 3];
var anotherArray = anArray;
Both these variables point to the same array in the memory.
This question discusses the ways to duplicate an array.
You can create a new array with a new reference like so:
var numbersTest = new Array(numbers);
This might be a simpler solution though:
function removeSmallest(numbers) {
numbers.sort().splice(0, 1);
return numbers;
}
The removeSmallest() function is flawed though as integers are sorted alphabetically in javascript. A better answer would be:
function removeSmallest(numbers) {
numbers.sort(sortNumber).splice(0,1);
return numbers;
}
function sortNumber(a,b) {
return a - b;
}
Thanks to adeneo and Derek I think I understand the problem.
Basically, JavaScript objects (arrays such as my numbers array are one type of object) are assigned through "value of a reference" (unlike simple data types which are assigned through "value of a value").
What this means is that all variables assigned to a certain object hold the value of that object through reference to the original (i.e. through pointing to the original object) and not by having their own standalone value.
If the original is changed, the variable pointing to the original will have its value changed too (and vice versa).
To clone an array (which is what I wanted to do with my numbers array) you should simple use var numbersClone = numbers.slice(0). Reading this will explain.
In JavaScript, you can have objects, like this:
var a = { foo: 12, bar: 34 };
Or arrays with key (named) indexes, like this:
var b = [];
b['foo'] = 56;
b['bar'] = 78;
They're somewhat similar, but obviously not the same.
Now the strange thing is, JSON.stringify doesn't seem to take the array. No errors or anything, JSON.stringify(b) just results in [].
See this jsfiddle example. Am I doing something wrong, or do I just misunderstand how arrays work?
Javascript doesn't support Associative arrays (Like PHP).
var b = []; Declaring explicitly an array, when you are trying to create an Object.
Arrays in Javascript can only contain the Index approach of Arrays, while Objects are more of
Associative arrays.
If you change var b = []; to var b = {}; it will solve the problem.
var b = {} Declaring explicitly an Object.
Javascript arrays are objects. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Predefined_Core_Objects#Array_Object for details.
Note: if you supply a non-integer value to the array operator in the
code above, a property will be created in the object representing the
array, instead of an array element.
JSON supports only a subset of Javascript. See http://www.json.org/ for details.
JSON is built on two structures:
A collection of name/value pairs. In various languages, this is realized as an object, record, struct, dictionary, hash table, keyed
list, or associative array.
An ordered list of values. In most languages, this is realized as an array, vector, list, or sequence.
A Javascript array that has properties created in the underlying object does not fit into either of these structures because it has both a collection of name/value pairs and an ordered list of values, so there is no simple way to represent such an object directly in JSON.
The JSON.stringify method is defined in the ECMAScript specification. For example, see http://www.ecma-international.org/ecma-262/5.1/#sec-15.12.3.
While there are many details, the bit that is relevant here is how object values are stringified:
If Type(value) is Object, and IsCallable(value) is false
If the [[Class]] internal property of value is "Array" then Return the result of calling the abstract operation JA with argument value.
Else, return the result of calling the abstract operation JO with argument value.
Given your array, despite the addition of parameters to the underlying object, the result is of stringifying the ordered set of array elements, not the underlying object.
There is nothing wrong about adding parameters to an array object, but they are not part of the array and functions or methods that handle arrays might ignore them or deal with them arbitrarily. You have seen that JSON.stringify ignores the additional parameters. Other functions might do otherwise - you will have to find out in each case.
While it is not wrong, it will probably be easier to understand if you do not add properties to array objects. If you want to add properties, start with a non-array object.
Rather than:
var b = [];
b['foo'] = 56;
b['bar'] = 78;
You might use:
var b = {};
b['foo'] = 56;
b['bar'] = 78;
This snap is from IE explorer. See the array is still blank.
Actually the way of inserting the elements to the array is :
1. Use push()
2. insert the elements in the array during declaration
If you want to stringify the array you have to have the data inside the array.
So, now you want to stringify the key value pairs so you have to pass the object as the argument of JSON.stringify() as follows:
var test = {}; // Object
test['a'] = 'test';
test['b'] = []; // Array
test['b'].push('item');
test['b'].push('item2');
test['b'].push('item3');
var json = JSON.stringify(test);
alert(json);
Solution to your problem now:
Note: Console of Google Chrome is giving different result, which is a bug in Google Chrome.