I have declared an array a = [1,2,3,4,5]
When I write a[1] it returns 2 which is perfectly fine but when I write a['1'] it also gives me 2 instead of giving an error.
I was expecting an error there. Why does it behave like that??
All property names are strings.
If you pass a number, it gets converted to a string before being used to look up the property value.
console.log(1 == '1');
In JS an Array is basically an Object and thus mostly behaves like one. In this case, these are equivalent as long as you dont access Array's .length or try iterating a:
const a = {"0": foo};
const b = ["foo"];
Also this would work:
const a = ["foo"];
a.bar = "baz";
console.log(a);
So that a[1] and a['1'] are equivalent is exactly what's to be expected.
First of all, array is also object having property names as 0,1,2,....n
Property names must be strings. This means that non-string objects cannot be used as keys in the object. Any non-string object, including a number, is typecasted into a string via the toString method. [Ref]
Related
var hello = 'hello';
Array.prototype.unshift.call(hello, '11') // gives error
Array.prototype.join.call(hello, ', ') // works, why??
can someone explain to me why .join works and why .unshift doesn't
Because strings are immutable, and unshift tries to assign to an index (property) of the string, as in
"hello"[4] = '1'
Reference: http://www.ecma-international.org/ecma-262/6.0/#sec-string-exotic-objects:
A String object is an exotic object that encapsulates a String value and exposes virtual integer indexed data properties corresponding to the individual code unit elements of the String value. Exotic String objects always have a data property named "length" whose value is the number of code unit elements in the encapsulated String value. Both the code unit data properties and the "length" property are non-writable and non-configurable.
join doesn't assign anything and only reads properties, therefore it works with any object that has .length.
Try this:
String.prototype.unshift = function(el) {
let arr = [this];
arr.unshift(el);
return arr.join("");
}
var s = "BCD";
s = s.unshift("A");
console.log(s); // ABCD
This may be completely stupid but given I type this into my browser console:
var arr = [];
arr.item = 'val';
console.log( arr );
arr;
What is the arr; syntax doing behind the scenes? I'm assuming console.log( arr ); is iterating all the properties of the arr object but what is arr; doing? Also does [] tell me I'm dealing with an object of type array and {} tells me I'm dealing with an object of type object literal? So [ item: 'val' ] is an object with a prototype of array and { item: 'val' } is an object of prototype object literal?
Edit: Another way to ask this might be why are console.log and arr; different?
Sorry, started to type the comment and realized it was actually a full answer.
console.log yields an implementation dependent string representation of the argument. It does not iterate the properties of an object in most I've seen. String serializtions using the default toString method look like [object Foo] where 'Foo' is the internal class property of the object. You can see this for arrays by using Object.prototype.toString.call([]);.
The last line on the other hand, is just the value. How that gets represented as an evaluated expression in your browser console is implementation-dependent, but for all intents and purposes it does nothing and there is no reason you would want to write that.
Note that adding a property to an array with the . operator is almost never what you want: it does not add to the contents of the array but treats the array as an object. Iterating the indicies of the array will not yield that property, nor will its methods such as .map include it. Use .push. If you want string-keyed properties, use an object.
What is the arr; syntax doing behind the scenes?
This is an ExpressionStatement. The JavaScript engine tries to evaluate the expression by reading the value of the variable, and then does nothing with it. If it occures inside a javascript function, it will do nothing (except when arr is not a variable, it will throw an error). If you just write arr; into the developer console, most browsers will echo the value of the last typed in expression, so you will see the value of arr as a result. The echoing mechanism is often implemented similar to console.log;
I'm assuming console.log( arr ); is iterating all the properties of the arr object
console.log prints a value to the developer console. If the value is a string, then the string gets printed. If the value is an object, then it is browser-dependent what happens. Most browsers, like Chrome will show an interactive representation of the object, like this:
Other browser might just call toString on the object, which in fact will iterate over the elements, and join them by commas.
Also does [] tell me I'm dealing with an object of type array and {} tells me I'm dealing with an object of type object literal?
Yes and yes. [1, 2, 3] is an array literal, which will evaluate to a new instance of an array containing those values. [] is just an empty array. Arrays are instances of the type Array. {a:1,b:2} is an object literal, will evaluate to a new instance every time. {} is an empty object with no fields. Object literals evaluate to instances of the type Object.
So [ item: 'val' ] is an object with a prototype of array and { item: 'val' } is an object of prototype object literal?
No and yes. [ item: 'val' ] is invalid syntax. Array literals have elements separated by commas, they cannot have colons in them.
[ item: 'val' ]
VM154:1 Uncaught SyntaxError: Unexpected token :(…)
The : is the unexpected token, the expression can't be evaluated because it has syntax errors.
On the other hand { item:'val' } is a completely regular object literal with type Object and will have a single field named item with value 'val'.
About object properties
In javascript, object do not have a fixed set of properties. Properties can be dynamically added and removed by writing them and by using the delete keyword. Every object has string (or Symbol) field names. An array can be seen as an object, optimized for integer keys. But still, keys are strings, so a[0] means the same as a["0"].
var arr = [1, 2];
arr.item = 'val'; // assigns a new field to the object, called item
arr['item'] = 'val'; // does exactly the same as the previous
arr[1] = 3; // sets the second item in arr to 3
arr['1'] = 3; // does exactly the same as the previous
console.log(arr);
// prints [1, 2, item: "val"]
So arbitrary named fields can be assigned on an array, and when printed, Chrome does notice the object being an array, so uses array notation, but also prints the 'other' assigned values. It's important to mention that not every field gets printed, as arr.length is a fully featured property. It is the way guys at Google chose to print arrays. It is not a valid JavaScript syntax, it is just a (pretty good) visualisation of data inside an array.
About classes and prototypes
We say that foo is of class Foo if foo has the prototype Foo.prototype, in other words foo.__proto__ === Foo.prototype. Here foo is an object, Foo is a constructor function. As Foo itself is a function, Foo.__proto__ === Function.prototype.
If subtypes are involved, then we can say bar is an instance of Foo, if bar has Foo.prototype in it's prototype chain, i.e.: bar.__proto__ === Foo.prototype || bar.__proto__.__proto__ === Foo.prototype || .... Explicitly accessing an object's .__proto__ property is considered a bad practice and is strongly discouraged. Altering the __proto__ value leads to serious performance drop, because it breaks the browser's optimisation strategies. So how to check if an object is of a type? You can use the instanceof operator.
Unintentionally, In my project I had used the following code and I was surprised to see that is working:
HTML
<span id="output"></span>
Javascript
var myObject = {
'a': '----First---',
'b': '----Second---',
'c': '----Third----'
};
var myArray = ['a'];
// First Case
output.innerHTML = myObject[myArray];
var myArray2 = ['b'];
// Second Case
output.innerHTML += myObject[myArray2];
var myArray3 = ['a', 'b'];
// Third Case
output.innerHTML += myObject[myArray3];
Output
----First-------Second---undefined
Jsbin Link: http://jsbin.com/godilosifu/1/edit?html,js,output
I am directly accessing array reference within Object which should be undefined in all the cases but strangely When array is of Size 1, it always gets the first element and use that as the object key.
I just want to know what is this concept called and why is this happening ?
Because the property name has to be a string, it is type cast into one using the toString() method. The reason that your third example is undefined is that ['a', 'b'].toString() equals 'a,b', which is not a property in your object.
Property names
Property names must be strings. This means that non-string objects
cannot be used as keys in the object. Any non-string object, including
a number, is typecasted into a string via the toString method.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors
Worth noting is that in ECMAScript 6, there is a new collection type called Map, which allows you to use any object as a key without type coercion.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
When you pass the array to the object as a Key, it is calling toString() on it. This is because all Keys in Javascript are Strings.
['a'].toString() is "a"
myObject['a'] is "----First----"
See: Keys in Javascript objects can only be strings?
It's auto-casting. In Javascript, only strings can be indexes into an object's properties. myObject is not an array, it's an object. Even though you're using the brackets to access it's properties, it is not the same meaning as brackets used on an array object.
Due to object properties only being allowed to be named by strings, the compiler auto-casts your array to a string (essentially calling it's native toString() function, which for an array, joins all the elements with a comma).
So when you pass your array to the object's property accessor/index, it does this:
myArray1 ==> "a";
myArray2 ==> "b";
myArray3 ==> "a" + "," + "b" ===> "a,b";
I'm not supposed to add elements to an array like this:
var b = [];
b.val_1 = "a";
b.val_2 = "b";
b.val_3 = "c";
I can't use native array methods and why not just an object. I'm just adding properties to the array, not elements. I suppose this makes them parallel to the length property. Though trying to reset length (b.length = "a string") gets Uncaught RangeError: Invalid array length.
In any case, I can still see the properties I've set like this:
console.log(b); //[val_1: "a", val_2: "b", val_3: "c"]
I can access it using the dot syntax:
console.log(b.val_1); //a
If an array is just an object in the same way a string or a number is an object, why can't (not that I'd want to) I attach properties to them with this syntax:
var num = 1;
num.prop_1 = "a string";
console.log(num); //1
I cannot access its properties using dot syntax
console.log(num.prp); //undefined
Why can this be done with array and not with other datatypes. For all cases, I should use {} and would only ever need to use {}, so why have arrays got this ability?
JSBIN
Because arrays are treated as Objects by the language, you can see this by typing the following line of code:
console.log(typeof []) // object
But other types like number literals, string literals NaN ... etc are primitive types and are only wrapped in their object reprsentation in certain contexts defined by the language.
If you want to add properties or methods to a number like that, then you can use the Number constructor like this:
var num = new Number(1);
num.prop_1 = "fdadsf";
console.log(num.prop_1);
Using the Number constructor returns a number object which you can see by typing the following line:
console.log(typeof num); // object
While in the first case:
var num = 1;
console.log(typeof num) // number
EDIT 2: When you invoke a method on a number literal or string literal for instance, then that primitive is wrapped into its object representation automatically by the language for the method call to take place, for example:
var num = 3;
console.log(num.toFixed(3)); // 3.000
Here num is a primitive variable, but when you call the toFixed() metohd on it, it gets wrapped to a Number object so the method call can take place.
EDIT: In the first case, you created a string like this first var str = new String(), but then you changed it to str = "asdf" and then assigned the property str.var_1 = "1234".
Of course, this won't work, because when you assigned str = "asdf", str became a primitive type and the Object instance that was originally created is now gone, and you can't add properties to primitives.
In the second, it didn't output undefined like you said, I tested it in Firebug and everything worked correctly.
EDIT 3:
String literals (denoted by double or single quotes) and strings returned from String calls in a non-constructor context (i.e., without using the new keyword) are primitive strings.
This is taken from MDN Documentation, when you use string like that var p = String(3) it becomes a conversion function and not a constructor and it returns a primitive string as you can see from the quote above.
Regarding your second comment, I didn't understand how my comment has been defied, because if you try to console.log(p.pr) you'll get undefined which proves p is a primitive type and not an object.
If an array is just an object in the same way a string or a number is an object,
An array is different than strings, numbers, booleans, null and undefined. An array IS an object, while the others in the list are primitive values. You can add properties to the array just like you would with any other object, anything different being just what makes arrays special (the length property you mentioned for example). You cannot add properties or call methods on primitive values.
In this previous answer i talked about the use of Object wrappers over primitive values. Feel free to read it to see more about Object wrappers. The following will be a very short example:
console.log('TEST'.toLowerCase()) // 'test'
While it may seem that the toLowerCase method is called on the string 'TEST', in fact the string is converted automatically to a String object and the toLowerCase method is called on the String object.
Each time a property of the string, number or boolean is called, a new Object wrapper of the apropriate type is created to get or set that value (setting properties might be optimized away entirely by the browser, i am not sure about that).
As per your example:
var num = 1; // primitive value
num.prop_1 = "a string"; // num is converted to a Number, the prop_1 property is set on the Object which is discarded immediately afterwards
console.log(num); //1 // primitive value
console.log(num.prp); // num is converted to a Number, which doesn't have the prp property
My example:
Number.prototype.myProp = "works!";
String.prototype.myFunc = function() { return 'Test ' + this.valueOf() };
Boolean.prototype.myTest = "Done";
console.log(true.myTest); // 'Done'
console.log('really works!'.myFunc()); // 'Test really works!'
var x = 3;
console.log(x.myProp.myFunc()); // 'Test works!'
console.log(3['myProp']); // 'works!'
On the other hand:
console.log(3.myProp); // SyntaxError: Unexpected token ILLEGAL
The number isn't necessarily treated differently, that syntax just confuses the parser. The following example should work:
console.log(3.0.myProp); // 'works!'
What is the JavaScript equivalent function for CreateObject("Scripting.Dictionary")?
I have to convert following two statements from VBScript to JavaScript, anyone can help me to find a solution.
Set oInvoicesToCreate = CreateObject("Scripting.Dictionary")
If Not oInvoicesToCreate.Exists(cInvoiceID) Then
oInvoicesToCreate(CStr(cInvoiceID)) = ""
End If
var oInvoicesToCreate = {};
if(oInvoicesToCreate[cInvoiceID] === undefined){
oInvoicesToCreate[cInvoiceID] = "";
}
You probably don't want to check the hasOwnProperty method because you'll want to check if anything in the prototype chain has that property as well and not overwrite it. checking with the []s will let you know if any property on any prototype items have the property as well.
As bluetoft says in this answer, in Javascript you can use a plain object instead. However, there are a few differences between them that you should be aware of:
First, a Dictionary's keys can be any type:
var dict = new ActiveXObject('Scripting.Dictionary');
dict(5) = 'Athens';
console.log(dict('5')); //prints undefined
whereas any value used for a Javascript object's key will be converted to a string first:
var obj = {};
obj[5] = 'Athens';
console.log(obj['5']); // prints 'Athens'
From MDN:
Please note that all keys in the square bracket notation are converted to String type, since objects in JavaScript can only have String type as key type. For example, in the above code, when the key obj is added to the myObj, JavaScript will call the obj.toString() method, and use this result string as the new key.
Second, it is possible to set a Dictionary to treat differently cased keys as the same key, using the CompareMode property:
var dict = new ActiveXObject('Scripting.Dictionary');
dict.CompareMode = 1;
dict('a') = 'Athens';
console.log(dict('A')); // prints 'Athens'
Javascript key access via [] doesn't support this, and if you want to treat differently-cased keys as the same, you'll have to convert the potential key to lowercase or uppercase before each read or write.
For your specific scenario, neither of these differences matter, because the keys are numeric strings (1) which have no case (2).