Someone I know is just learning programming and stumbled upon this and left me baffled:
Please open a console (Chrome/Firefox) and type: var name = ['what', 'the', '...?'];
I would expect name to be an array of strings, but:
typeof name displays string instead of Array
listing the name variable prints a string instead of an array
name.length is 13 instead of 3
writing name = name.split(',') returns an array ["what", "the", "...?"] as expected, but name is still a string, not an array
name is the only variable name that seems to behave this way, or at least I couldn't find another one.
Is this just a console quirk, a JavaScript engine bug, or what?
NOTE: the above happens on Chrome and Firefox. IE Edge surprisingly works as expected (typeof name is Array and all that). Not tested on other browsers.
window.name is a global which is a string in the DOM.
Notice you can get around it by declaring the variable in a function scope:
(function() {
var name = ['foo', 'bar'];
console.log(typeof name);
})();
As for why IE/Edge is different - its their interpretation of the spec and likely has been that way for years. Changing it now would be a breaking change.
Related
This bit of code I understand. We make a copy of A and call it C. When A is changed C stays the same
var A = 1;
var C = A;
console.log(C); // 1
A++;
console.log(C); // 1
But when A is an array we have a different sitiuation. Not only will C change, but it changes before we even touch A
var A = [2, 1];
var C = A;
console.log(C); // [1, 2]
A.sort();
console.log(C); // [1, 2]
Can someone explain what happened in the second example?
Console.log() is passed a reference to the object, so the value in the Console changes as the object changes. To avoid that you can:
console.log(JSON.parse(JSON.stringify(c)))
MDN warns:
Please be warned that if you log objects in the latest versions of Chrome and Firefox what you get logged on the console is a reference to the object, which is not necessarily the 'value' of the object at the moment in time you call console.log(), but it is the value of the object at the moment you open the console.
Pointy's answer has good information, but it's not the correct answer for this question.
The behavior described by the OP is part of a bug that was first reported in March 2010, patched for Webkit in August 2012, but as of this writing is not yet integrated into Google Chrome. The behavior hinges upon whether or not the console debug window is open or closed at the time the object literal is passed to console.log().
Excerpts from the original bug report (https://bugs.webkit.org/show_bug.cgi?id=35801):
Description From mitch kramer 2010-03-05 11:37:45 PST
1) create an object literal with one or more properties
2) console.log that object but leave it closed (don't expand it in the console)
3) change one of the properties to a new value
now open that console.log and you'll see it has the new value for some reason, even though it's value was different at the time it was generated.
I should point out that if you open it, it will retain the correct value if that wasn't clear.
Response from a Chromium developer:
Comment #2 From Pavel Feldman 2010-03-09 06:33:36 PST
I don't think we are ever going to fix this one. We can't clone object upon dumping it into the console and we also can't listen to the object properties' changes in order to make it always actual.
We should make sure existing behavior is expected though.
Much complaining ensued and eventually it led to a bug fix.
Changelog notes from the patch implemented in August 2012 (http://trac.webkit.org/changeset/125174):
As of today, dumping an object (array) into console will result in objects' properties being
read upon console object expansion (i.e. lazily). This means that dumping the same object while
mutating it will be hard to debug using the console.
This change starts generating abbreviated previews for objects / arrays at the moment of their
logging and passes this information along into the front-end. This only happens when the front-end
is already opened, it only works for console.log(), not live console interaction.
The latest guidance from Mozilla as of February 2023:
Don't use console.log(obj), use console.log(JSON.parse(JSON.stringify(obj))).
This way you are sure you are seeing the value of obj at the moment you log it. Otherwise, many browsers provide a live view that constantly updates as values change. This may not be what you want.
Arrays are objects. Variables refer to objects. Thus an assignment in the second case copied the reference (an address) to the array from "A" into "C". After that, both variables refer to the same single object (the array).
Primitive values like numbers are completely copied from one variable to another in simple assignments like yours. The "A++;" statement assigns a new value to "A".
To say it another way: the value of a variable may be either a primitive value (a number, a boolean, null, or a string), or it may be a reference to an object. The case of string primitives is a little weird, because they're more like objects than primitive (scalar) values, but they're immutable so it's OK to pretend they're just like numbers.
EDIT: Keeping this answer just to preserve useful comments below.
#Esailija is actually right - console.log() will not necessarily log the value the variable had at the time you tried to log it. In your case, both calls to console.log() will log the value of C after sorting.
If you try and execute the code in question as 5 separate statements in the console, you will see the result you expected (first, [2, 1], then [1, 2]).
Though it's not going to work in every situation, I ended up using a "break point" to solve this problem:
mysterious = {property:'started'}
// prints the value set below later ?
console.log(mysterious)
// break, console above prints the first value, as god intended
throw new Error()
// later
mysterious = {property:'changed', extended:'prop'}
The issue is present in Safari as well. As others have pointed out in this and similar questions, the console is passed a reference to the object, it prints the value of the object at the time the console was opened. If you execute the code in the console directly for example, the values print as expected.
Instead of JSON stringifying, I prefer to spread arrays (e.g. in your case console.log([...C]);) and objects: the result is quite the same, but the code looks a bit cleaner. I have two VS code snippets to share.
"Print object value to console": {
"prefix": "clo",
"body": [
"console.log(\"Spread object: \", {...$0});"
],
"description": "Prints object value instead of reference to console, to avoid console.log async update"
},
"Print array value to console": {
"prefix": "cla",
"body": [
"console.log(\"Spread array: \", [...$0]);"
],
"description": "Prints array value instead of reference to console, to avoid console.log async update"
}
In order to get the same output as with console.log( JSON.parse(JSON.stringify(c))), you can leave out the string part if you wish. Incidentally, the spread syntax often saves time and code.
This bit of code I understand. We make a copy of A and call it C. When A is changed C stays the same
var A = 1;
var C = A;
console.log(C); // 1
A++;
console.log(C); // 1
But when A is an array we have a different sitiuation. Not only will C change, but it changes before we even touch A
var A = [2, 1];
var C = A;
console.log(C); // [1, 2]
A.sort();
console.log(C); // [1, 2]
Can someone explain what happened in the second example?
Console.log() is passed a reference to the object, so the value in the Console changes as the object changes. To avoid that you can:
console.log(JSON.parse(JSON.stringify(c)))
MDN warns:
Please be warned that if you log objects in the latest versions of Chrome and Firefox what you get logged on the console is a reference to the object, which is not necessarily the 'value' of the object at the moment in time you call console.log(), but it is the value of the object at the moment you open the console.
Pointy's answer has good information, but it's not the correct answer for this question.
The behavior described by the OP is part of a bug that was first reported in March 2010, patched for Webkit in August 2012, but as of this writing is not yet integrated into Google Chrome. The behavior hinges upon whether or not the console debug window is open or closed at the time the object literal is passed to console.log().
Excerpts from the original bug report (https://bugs.webkit.org/show_bug.cgi?id=35801):
Description From mitch kramer 2010-03-05 11:37:45 PST
1) create an object literal with one or more properties
2) console.log that object but leave it closed (don't expand it in the console)
3) change one of the properties to a new value
now open that console.log and you'll see it has the new value for some reason, even though it's value was different at the time it was generated.
I should point out that if you open it, it will retain the correct value if that wasn't clear.
Response from a Chromium developer:
Comment #2 From Pavel Feldman 2010-03-09 06:33:36 PST
I don't think we are ever going to fix this one. We can't clone object upon dumping it into the console and we also can't listen to the object properties' changes in order to make it always actual.
We should make sure existing behavior is expected though.
Much complaining ensued and eventually it led to a bug fix.
Changelog notes from the patch implemented in August 2012 (http://trac.webkit.org/changeset/125174):
As of today, dumping an object (array) into console will result in objects' properties being
read upon console object expansion (i.e. lazily). This means that dumping the same object while
mutating it will be hard to debug using the console.
This change starts generating abbreviated previews for objects / arrays at the moment of their
logging and passes this information along into the front-end. This only happens when the front-end
is already opened, it only works for console.log(), not live console interaction.
The latest guidance from Mozilla as of February 2023:
Don't use console.log(obj), use console.log(JSON.parse(JSON.stringify(obj))).
This way you are sure you are seeing the value of obj at the moment you log it. Otherwise, many browsers provide a live view that constantly updates as values change. This may not be what you want.
Arrays are objects. Variables refer to objects. Thus an assignment in the second case copied the reference (an address) to the array from "A" into "C". After that, both variables refer to the same single object (the array).
Primitive values like numbers are completely copied from one variable to another in simple assignments like yours. The "A++;" statement assigns a new value to "A".
To say it another way: the value of a variable may be either a primitive value (a number, a boolean, null, or a string), or it may be a reference to an object. The case of string primitives is a little weird, because they're more like objects than primitive (scalar) values, but they're immutable so it's OK to pretend they're just like numbers.
EDIT: Keeping this answer just to preserve useful comments below.
#Esailija is actually right - console.log() will not necessarily log the value the variable had at the time you tried to log it. In your case, both calls to console.log() will log the value of C after sorting.
If you try and execute the code in question as 5 separate statements in the console, you will see the result you expected (first, [2, 1], then [1, 2]).
Though it's not going to work in every situation, I ended up using a "break point" to solve this problem:
mysterious = {property:'started'}
// prints the value set below later ?
console.log(mysterious)
// break, console above prints the first value, as god intended
throw new Error()
// later
mysterious = {property:'changed', extended:'prop'}
The issue is present in Safari as well. As others have pointed out in this and similar questions, the console is passed a reference to the object, it prints the value of the object at the time the console was opened. If you execute the code in the console directly for example, the values print as expected.
Instead of JSON stringifying, I prefer to spread arrays (e.g. in your case console.log([...C]);) and objects: the result is quite the same, but the code looks a bit cleaner. I have two VS code snippets to share.
"Print object value to console": {
"prefix": "clo",
"body": [
"console.log(\"Spread object: \", {...$0});"
],
"description": "Prints object value instead of reference to console, to avoid console.log async update"
},
"Print array value to console": {
"prefix": "cla",
"body": [
"console.log(\"Spread array: \", [...$0]);"
],
"description": "Prints array value instead of reference to console, to avoid console.log async update"
}
In order to get the same output as with console.log( JSON.parse(JSON.stringify(c))), you can leave out the string part if you wish. Incidentally, the spread syntax often saves time and code.
Until recently, I didn't realise that there are two types of strings (and bools, and numbers) in JavaScript: primitives such as "blah", and objects such as new String("blah").
They differ in very "gotcha"-prone ways, the biggest one of which seems to be the different typeof value ("string" vs "object"), but a number of other differences exist, some documented at MDN.
There's no point in creating String objects because the primitive strings work just as well, and JSHint even treats this as an error. So I'd really like to pretend String instances don't even exist, and only support primitive strings in my code.
This makes me wonder: can I get a surprise String instance by calling some standard API that returns a string? Or is it really safe to assume that unless someone screws up by explicitly writing new String, I will never see one of these?
This github search shows that some people are using new String.
And this answer has a nice explanation for String obj:
No native JavaScript object, major library or DOM method that returns
a string will return a String object rather than a string value.
However, if you want to be absolutely sure you have a string value
rather than a String object, you can convert it as follows:
var str = new String("foo");
str = "" + str;
I am not aware of any convention to only use primitive strings. There is also an ever growing amount of API's available, and I'm pretty sure every now and then someone will pull the constructor trick. I would like to give some debugging advice on how to spot this quicky, but I think the typeof check is the only obvious way to quicky note the difference.
You could use a check like this answer, although I think it is not worth the trouble:
function isString(string){
return typeof myVar == 'string' || myVar instanceof String;
};
isString("foo"); //returns true
isString(new String("bar")); //returns true
isString({}); //returns false;
Conclusion: I'm afraid you just have to pray that you don't encounter
these quirky corner cases.
This question already has answers here:
Using the variable "name" doesn't work with a JS object
(4 answers)
Closed 7 years ago.
Lets say we have this code segment :
var name = ["Apples","Oranges","Strawberries"];
console.log(name.length);
This code produces this weird result of 27 !! The issue seems to be with using the variable name as 'name' which seems like a reserved keyword.
But can anyone explain why this weird behavior ?
It refers to window.name, which is the name of the window.
You can use the window's name to target hyperlinks, but it's not typically useful.
More info on window.name: https://developer.mozilla.org/en-US/docs/Web/API/Window.name
Just testing in chrome:
You can't stop var name from being window.name, which is a string. No matter what you set the value to, it will be cast as a string so that it is a valid window name. So, name.length is the amount of characters in the string. It's best to avoid variable or be very careful about them!
As I can see from some of the other comments, this is an odd concept if you're new to it. The concern is over what window.name refers to. window.name is the name of the window. Any use of it is naming the window.
Defending that Chrome's behavior is logical:
If this var document = 'foo' did what it looks like it would do, you would have overwritten window.document - the document object - with a string. That would be quite the problem. name is a property of window just like document and has a use that shouldn't be (and can't be in Chrome) replaced.
In the global scope, when you do var name = ["Apples","Oranges","Strawberries"];, it's the same as window.name = ["Apples","Oranges","Strawberries"];.
window.name must be a string, so it assign ["Apples","Oranges","Strawberries"].toString() to it which is "Apples,Oranges,Strawberries".
What is the scope of variables in JavaScript? is a good start. Basically, when you're declaring name outside of a function, you're actually hiding the window.name value, which is a very, very bad idea (be very careful about any global variables you declare - they are actually values of the window object - including hiding existing values.
As others have said, the fact that Google Chrome forces the type to string is probably a Chrome quirk (although somewhat understandable), but the underlying cause is that you're simply doing something "dangerous" you didn't realize you're doing :)
the window object has a property "name";
if you define 'name' in a closure function you may get 3
you defined var name in the global object, so you just initialized a string obj;
(function(){
var name = ["Apples","Oranges","Strawberries"];
consol.log(name.length);
})()
var name = ["Apples","Oranges","Strawberries"];
the global window.name is a build in string object(reserved), so name.toString() is called when you excuete the line above;
As pointed out by others, in the when you are using the variable named name in the global scope it is treated as window.name.
From experimentation under Google Chrome, window.name gets the value as a string, like so: "Apples,Oranges,Strawberries", This behaviour is because Google Chrome is forcing window.name to be string. This explains why name.length will report 27 as that is the length of the string given.
This behaviour is not present in IE, Opera or Firefox which assing the array ["Apples","Oranges","Strawberries"] and report 3 on `name.length as expected, therefore I guess this is a quirk of Google Chrome.
I have a method, that perfectly works in Firefox, with which I can determine the name of an instance of a particular javascript object (please don't ask why I need it...).
Fr example:
var temp = new String("hello!");
var theName = getVarName(temp); //returns "temp"
This method uses "window.hasOwnProperty()" which doen't work in Internet Explorer: any suggestions?
If for whatever reason you do need to use window, use:
Object.prototype.hasOwnProperty.call(obj, p)
I have a method, that perfectly works in Firefox, with which I can determine the name of an instance of a particular javascript object
I don't think you do, because that's not possible in JavaScript. JS is a call-by-value language; when you write:
var temp= 'hello';
getVarName(temp);
that's exactly the same as saying:
getVarName('hello');
At which point the reference to ‘temp’ as a variable is lost. What I'm guessing your getVarName function does is basically this:
function getVarName(value) {
for (var name in window) {
if (window[name]===value)
return name;
}
}
This will work on IE and other browsers without Object.hasOwnProperty(); it will simply return the name of any global variable that matches the argument. The hasOwnProperty() call can be added to this function to refine it a little by only allowing direct properties of window (which act as global variables, including the ones you set explicitly), and not of any of its prototypes. I'm guessing that's what your version of the function is doing, but in practice it has very little effect since almost nothing inherits into ‘window’ by prototype.
You're confusing things a little bit by boxing your 'hello' in an explicit String object (which is very unusual and rarely a good idea), which makes it possible to have two different 'hello' objects that are disinguishable using the === identity comparator, so this will work:
var a= new String('hello!');
var b= new String('hello!');
getVarName(a); // 'a'
getVarName(b); // 'b' - distinguishable object from a
But that still doesn't stop you from doing:
var a= new String('hello!');
var b= a;
getVarName(a); // 'a' or 'b', depending on implementation
getVarName(b); // the same 'a' or 'b' as in the previous call
So, whilst you can fairly harmlessly lose the hasOwnProperty() call as above, what you're doing can't really work properly and you should probably look at a better way of achieving whatever it is you're up to.
Do not use global variables. Always put them in namespaces so that you don't run into such problems.
If I understood correctly, you want the global variable name when given the value under any scope. For example, the following should work too
var temp = "hello";
function foo(x) {
alert(getVarName(x)); //returns "temp" as well.
}
foo(temp);
This is sort of reverse hash lookup on Window object which is not only expensive, since you need to iterate over all the properties of window object each time, but also unreliable (two variables can have a same value).