5

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

1252748
  • 14,597
  • 32
  • 109
  • 229

2 Answers2

7

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.

Ibrahim Najjar
  • 19,178
  • 4
  • 69
  • 95
  • Why does using the constructor make any difference? – 1252748 Nov 15 '13 at 23:54
  • 1
    @thomas Because when you use the constructor a number object is returned as you can see by typing `console.log(typeof num)`. – Ibrahim Najjar Nov 15 '13 at 23:55
  • What would be the advantage of making the new number an object? Is there some reason new Number() doesn't create a primitive number? – 1252748 Nov 16 '13 at 00:19
  • 1
    @thomas Primitive types are used in all languages so they don't cause the overhead of object creation, but their object counterparts are provided so that those primitives could be wrapped into objects when that is needed *(and it happens a lot)*. Also all the methods defined on numbers and strings are defined on prototype of `Number` and `String` so when you call a method on a primitive number or string, it is first wrapped into its object representation. – Ibrahim Najjar Nov 16 '13 at 11:23
  • Sniffer, can you show a couple examples of primitives needing to be wrapped into objects? Thanks so much – 1252748 Nov 16 '13 at 15:47
  • 1
    @thomas I have edited my answer to cover your latest request, please check edit. – Ibrahim Najjar Nov 16 '13 at 16:18
  • Why does this not show me the property I added with the dot? http://jsbin.com/iWuYAgE/1/edit. – 1252748 Nov 18 '13 at 15:30
  • and this is undefined var str = `new String(); str.val_1 = "asdf"; console.log("printed with dot=>" + str.val_1);` Seems like it should print the value i assigned.. – 1252748 Nov 18 '13 at 15:53
  • 1
    @thomas I have edited my answer to cover your last comments, please check. – Ibrahim Najjar Nov 18 '13 at 20:35
  • Sniffer, sorry one more question. Why doesn't this typeof show object, but rather string: `var p = 1; p = String(p); console.log(typeof p); //string` – 1252748 Nov 20 '13 at 22:21
  • Also, `p.pr = "asdf"; console.log(typeof p); //string` seems to defy your example: `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` – 1252748 Nov 20 '13 at 22:24
  • 1
    @thomas I have edited my answer to cover your latest comments, please review. – Ibrahim Najjar Nov 21 '13 at 08:59
  • Thanks for continuing to help me understand this. I guess I understood that if I used the constructor to create a string, my variable would be instantiated with an `object` wrapper. I expected to see that, but instead saw string, making me thing it was still a primitive type. – 1252748 Nov 21 '13 at 15:12
  • given `var p = String(3) p.v = "asdf"; console.log(typeof p))//string` What would I need to do to this to make `typeof` return `object` – 1252748 Nov 21 '13 at 15:22
  • 1
    @thomas You would need `var p = new String(3)` instead. – Ibrahim Najjar Nov 21 '13 at 15:28
  • `String("text")` is just a method. `new String("test")` is a constructor with an object wrapper? Have I finally gotten it correct? – 1252748 Nov 21 '13 at 15:33
  • 1
    @thomas Yes you got it, read more at the link I gave you in my final edit. – Ibrahim Najjar Nov 21 '13 at 15:34
  • Sniff, I upvoted all of your comments because this is one of the most helpful and productive exchanges I've ever had on SO. And I had to wait five seconds between each comment vote, which was sort of really annoying..but worth it! Infinite thanks! :) – 1252748 Nov 21 '13 at 15:46
2

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!'
Community
  • 1
  • 1
Tibos
  • 27,507
  • 4
  • 50
  • 64