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.
Related
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
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() + '"');
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!'
I'm building a node scraper that uses cheerio to parse the DOM. This is more or a vanilla javascript question though. At one part of my scrape, I'm loading some content into a variable, then checking the variable's length, like so:
var theHref = $(obj.mainImg_select).attr('href');
if (theHref.length){
// do stuff
} else {
// do other stuff
}
This works just fine, until I came across a url for which $(obj.mainImg_select).attr('href') didn't exist. I assumed that my theHref.length check would account for this and skip through to the else: do other stuff statement, but instead I got:
TypeError: Cannot read property 'length' of undefined
What am I doing wrong here and how can I fix this?
You can check that theHref is defined by checking against undefined.
if (undefined !== theHref && theHref.length) {
// `theHref` is not undefined and has truthy property _length_
// do stuff
} else {
// do other stuff
}
If you want to also protect yourself against falsey values like null then check theHref is truthy, which is a little shorter
if (theHref && theHref.length) {
// `theHref` is truthy and has truthy property _length_
}
Why?
You asked why it happens, let's see:
The official language specificaion dictates a call to the internal [[GetValue]] method. Your .attr returns undefined and you're trying to access its length.
If Type(V) is not Reference, return V.
This is true, since undefined is not a reference (alongside null, number, string and boolean)
Let base be the result of calling GetBase(V).
This gets the undefined part of myVar.length .
If IsUnresolvableReference(V), throw a ReferenceError exception.
This is not true, since it is resolvable and it resolves to undefined.
If IsPropertyReference(V), then
This happens since it's a property reference with the . syntax.
Now it tries to convert undefined to a function which results in a TypeError.
There's a difference between an empty string "" and an undefined variable. You should be checking whether or not theHref contains a defined string, rather than its lenght:
if(theHref){
// ---
}
If you still want to check for the length, then do this:
if(theHref && theHref.length){
// ...
}
In addition to others' proposals, there is another option to handle that issue.
If your application should behave the same in case of lack of "href" attribute, as in case of it being empty, just replace this:
var theHref = $(obj.mainImg_select).attr('href');
with this:
var theHref = $(obj.mainImg_select).attr('href') || '';
which will treat empty string ('') as the default, if the attribute has not been found.
But it really depends, on how you want to handle undefined "href" attribute. This answer assumes you will want to handle it as if it was empty string.
If you aren't doing some kind of numeric comparison of the length property, it's better not to use it in the if statement, just do:
if(theHref){
// do stuff
}else{
// do other stuff
}
An empty (or undefined, as it is in this case) string will evaluate to false (just like a length of zero would.)
As has been discussed elsewhere, the .length property reference is failing because theHref is undefined. However, be aware of any solution which involves comparing theHref to undefined, which is not a keyword in JavaScript and can be redefined.
For a full discussion of checking for undefined variables, see Detecting an undefined object property and the first answer in particular.
You can simply check whether the element length is undefined or not just by using
var theHref = $(obj.mainImg_select).attr('href');
if (theHref){
//get the length here if the element is not undefined
elementLength = theHref.length
// do stuff
} else {
// do other stuff
}
I'm learning JavaScript and I found in the codebase an if statement which part of it is redundant.
if(undefined === value || null === value || !value.toString)
Is there something in js that doesn't have toString defined (apart from undefined and null)?
If the objective of that code is to check if the value variable has the toString method defined then it is strange, as the only case that would evaluate to false is if value is undefined.
I took the liberty to create a fiddle for you (http://jsfiddle.net/gnrcc/2/) with the following code:
var obj = new String("Hi, im an object"); // String object
var str = "Hello, im a primitive" // string primitive
var und; // undefined
// will output native method, string and object details
console.log(obj.toString, obj.toString(), obj);
// will output native method, string and string
console.log(str.toString, str.toString(), str);
// will get property undefined error
console.log(und.toString, und.toString(), und);
All Objects have the toString method (from the Object prototype)
All Primitives will get automatically wrapped by theyr corresponding Object when a method is called on them, resulting in the same behaviour as Objects (Edit: the exception is obviously the undefined primitive)