JS String constructor principle - javascript

var x = undefined;String(x); // "undefined"
var y = [x];String(y); // ""
Why above output different value?
It confuse me.
Why String(undefined) returns undefined but String([undefined]) returns ''?
What makes difference with the results?
Thanks!

When you call String([]), the method Array.prototype.toString is eventually called, and according to the spec of Array.prototype.toString, the steps are:
Let array be the result of calling ToObject on the this value.
Let func be the result of calling the [[Get]] internal method of array with argument "join".
If IsCallable(func) is false, then let func be the standard built-in method Object.prototype.toString (15.2.4.2).
Return the result of calling the [[Call]] internal method of func providing array as the this value and an empty arguments list.
You can figure out that it then actually calls Array.prototype.join and returns its result. The spec of Array.prototype.join is a little bit longer but the step 8 is the root cause confusing you:
If element0 is undefined or null, let R be the empty String; otherwise, Let R be ToString(element0).
Which means when Array.prototype.join iterates all of the elements, whenever it meets undefined or null, it'll replace it by empty String, and the default delimiter of join is ,. That's why String([undefined]) gives you "", because there's only one element in the array and it's undefined, if you try String([undefined, null]), you'll get ","(two empty string joined by ,), and String([null, undefined, 1]) will give you ",,1"
A little snippet to prove Array.prototype.toString calls Array.prototype.join:
console.log(String([undefined]))
console.log(String([1,2,3]))
console.log("----------Override Array.prototype.join--------")
Array.prototype.join = function() {
return "I don't care what is in the original array";
}
console.log(String([undefined]))
console.log(String([1,2,3]))
BTW, there's an awesome blog talking about how to convert value to String in Javascript, hope it helps.
Regards:-)

Javascript is a bit funny on casting.
On the first one, you are casting an undefined value. All it does is literally write that text.
On the second one, you are casting an array with values. Hence, the process is different. Try: var z = [x, x]; String(z); // returns ","
Same happens if you do:
var x = null; String(x);
var y = [x]; String(y);
What your assumption is that String([undefined]) will do is cast each value as String(), then concatenate them. However, strings are basically arrays of characters. If you have an array with nothing in it (i.e. undefined, null), it just makes an empty string.
There are other examples of funny "inconsistencies" like that:
[] + {} // returns [object Object]
{} + [] // returns 0

You could always just read ECMA-262.
When String is called as a function with one value, it returns (more or less) ToString(value).
Calling ToString(undefined) returns the string "undefined", which explains the first result.
Similarly for the the second case, except that since Arrays are Objects, calling ToString(arrayInstance) first calls ToPrimitive. That will call the array's toString method, which is effectively array.join(',').
Since the array only has one member and its value is undefined, join returns an empty string. Try it:
console.log( '"' + [undefined].toString() + '"');

Related

Can we say that String is an object in Javascript?

I'm always confused when I hear strings are primitives in JS, because everybody knows that string has different methods like: length, indexOf, search etc.
let string = "Please locate where 'locate' occurs!";
let pos = str.lastIndexOf("locate");
let position = str.search("locate");
It's true that everything in JavaScript is just like object because we can call methods on it. When we use new keyword with string it becomes an object otherwise it's primitive type.
console.log(typeof new String('str')); //object
console.log(typeof 'str'); //string
Now whenever we try to access any property of the string it box the the primitive value with new String()
'str'.indexOf('s')
is equivalent to
(new String(str)).indexOf('s').
The above process is called as "Boxing". "Boxing" is wrapping an object around a primitive value.
Strings are not objects, they are native types, like numbers, but if you want to access the method on it they are boxed with String object. The same happen with numbers you can call (10).toString() but in fact you're calling toString on Number instance that wraps 10, when you call the method.
Not certainly in that way.
If you try to use a class method for a primitive, the primitive will be temporarily wrapped in an object of the corresponding class ("boxing") and the operation will be performed on this object.
For example,
1..a = 2;
It's equal to:
num = new Object(1.);
num.a = 2;
delete num;
So, after executing the operation, the wrapper will be destroyed.
However, the primitive itself will remain unchanged:
console.log( 1..a ) // undefined
Every data type is a object in JavaScript.
String, array, null, undefined . Everything is a object in JavaScript

JS Array a['1'] doesnot gives an error

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]

adding properties to primitive data types other than Array

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!'

Why is this variable returning undefined?

var place = "mundo"["Hola", "Ciao"];
Why does this return undefined? Just because it is garbage?
That is perfectly valid JS, though it doesn't do what you expect.
place is initialized to the 'Ciao' property of String('mundo'). Since it doesn't exist, it is initialized to undefined.
The tricky part:
"Hola","Ciao" is using the comma operator, evaluates "Hola", evaluates "Ciao" and returns "Ciao"
[...] in this case is property access
"mundo"[] "mundo" is converted to a String object to access the property on it.
Proof:
var place = "mundo"["Hola", "toString"];
console.log(place) // function toString() { [native code] }
The array operator on a string object will either try to index into the string and return a specific character from that string (on some JS implementations) or it will try to lookup a property on that object. If the index is a number, some JS implementations (I think this is non-standard behavior) will give you that character from the string.
// returns "m" in Chrome
"mundo"[0]
// returns undefined
"mundo"[9]
But, an array index that isn't a number will try to look for that property on the string object and your particular value won't be found on the string object and thus you get undefined.
// does a property lookup and returns "function toString{[native code]}
"mundo"["toString"]
// returns undefined - no propery named foo
"mundo"["foo"]
So, since there is no property on the string that resembles anything in ["Hola", "Ciao"], you get undefined. Technically, the browser is actually looking for the "Ciao" property when you give it this and because that property doesn't exist, you get undefined.
In a weird test, you can run this code to sort of see what's going on:
var str = new String("mundo");
str["Ciao"] = "Hello";
alert(str["Hola", "Ciao"]); // alerts "Hello"
Working demo of this: http://jsfiddle.net/jfriend00/e6R8a/
This all makes me wonder what in the heck you are actually trying to do that comes up with this odd construct.

How exactly does the JavaScript expression [1 [{}]] parse?

Can you explain how the JavaScript expression:
[1 [{}]]
parses/evaluates? In Firefox, Chrome, Konqueror, and rhino, it seems to create an array with a single element, undefined. However, I don't understand why.
In Firefox:
[1 [{}]].toSource()
produces
[(void 0)]
Replacing 1 with other JavaScript values seems to yield the same result.
Update: I think I understand now. codeka, Adrian, and CMS clarified things. As far as the standard, I tried to walk through ECMAScript 5.
1 [{}] is a Property Accessor, so it's covered in §11.2.1.
baseReference is the result of evaluating 1, so still 1.
baseValue = GetValue(baseReference) == 1.
At GetValue (§8.7.1), Type(1) is not Reference (a resolved name binding), so return 1.
propertyNameReference is result of evaluating {}, so an empty object.
propertyNameValue = GetValue(propertyNameReference) == {}
At CheckObjectCoercible(baseValue) (§9.10), we return (Number is object-coercible).
propertyNameString = ToString(propertyNameValue)
At ToString (§9.8), return ToString(ToPrimitive({}, hint String))
At ToPrimitive (§9.1), return result of object's [[DefaultValue]], passing PreferredType (string).
At [[DefaultValue]] (§8.12.8), let toString be result of [[Get]] with argument toString.
This is defined at §15.2.4.2 to return "[object " + [[Class]] + "]", where [[Class]] is "Object" for the default object prototype.
Since there is a callable toString, we call it with argument this being {}.
Return a value of type Reference, whose base value is BaseValue (1) and whose referenced name is propertyNameString ("[object Object]").
We then go to Array initializer (§11.1.4), and construct a single element array with the result.
Reading the OP and Nick comments, I think I can expand a little bit more the Adrian's answer to make it clearer.
It's perfectly valid JavaScript.
JavaScript handles object property names as strings, objects cannot contain other types or other objects as keys, they are just strings.
The bracket notation property accessor (MemberExpression [ Expression ]) implicitly converts the expression between brackets into a string, so:
var obj = {};
obj[{}] = "foo";
alert(obj["[object Object]"]); // foo
In the above example you can see that I assign a value to the {} property, and {}.toString() (or {}+'') produces the string "[object Object] (via Object.prototype.toString).
The expression 1 [{}] implicitly converts the 1 Number primitive to an object (this is made by the property accessor) and it lookups a property named "[object Object]" the property lookup is made on the Number.prototype and the Object.prototype objects, for example:
1['toString'] === Number.prototype.toString; // true
Finally, the 1 [{}] expression is itself enclosed in brackets ([1 [{}]]), this is actually an Array literal.
In conclusion here is how the parser evaluates the expression:
[1 [{}]];
// ^ The accessor expression is evaluated and converted to string
[1 ["[object Object]"]];
// ^ A property lookup is made on an Number object
// trying to access a property named "[object Object]"
[undefined];
// ^ the property is obviously not found
[undefined];
//^ ^
// An array literal is created with an element `0` which its value is `undefined`
It is because you are trying to get the property {} of the object 1 and then place it in an array. 1 doesn't have the property {}, so 1[{}] is undefined.
If you replace the 1 with an array, you will see how it is working. With 1 as [5] and {} as 0, it is [[5][0]].
Also, keep in mind that obj['property'] is the same as obj.property.
If we break it up a bit, you'll see:
var foo = 1;
var bar = {};
var baz = foo[bar];
[baz];
I believe it's valid JavaScript, but I'm not an expert...

Categories

Resources