I have a recursive function which performs the following things(among others), in that order:
Prints the array A which is passed as a parameter
Concatenates some new values into it:
A=A.concat(localList);
Prints the array A again
Runs a for loop, each iteration of which calls the function again
While the print sandwich shows correct concatenation, I notice that different(parallel?) instances do not aknowledge the changes other make. Aren't arrays passed as reference?
I've included minimal info because I feel this is some basic fact I'm missing.
Array.concat returns a new instance. In order to keep the reference intact, you can concat a list like so:
yourarray.push.apply(yourarray, newitems)
Or more modern variant:
yourarray.push(...newitems)
To be clear, this will still not work if you're using multiple threads (Workers) since objects passed between Workers are cloned.
So I have came across a curious question that I can't find its answer anywhere and there isn't much documentation on what eval does when you pass to it string literals.
If I do eval("alert(1)") I will get an alert box with 1, however, when I do eval`alert(1)` I just get an array with "alert(1)" I am not sure where that is coming from, isn't it supposed to be treated the same as the previous example?
Also, eval`${1}` returns an array with two empty elements, why?
What you're running into is something to do with tagged templates.
Essentially, you are doing string interpolation and using a function to decide how to create the string. The first argument is expected to be an Array that contains all of the string parts (everything between ${var} declarations). The return of any function used this way is expected to be the string. The reason you are seeing 2 entries is because this function returns a raw format in addition to the one it tried to create using the tag function.
Not too long ago, I discovered that arrays in JavaScript need not contain an ordered set of keys (0-x) to store values within it
and some numeric keys may not be defined (0-4 ... 6-x, where 5 is not defined).
And this creates semantically two types of arrays that are similar:
arrayA = [, ,] (partially-empty arrays or sparse arrays)
arrayB = [undefined, undefined] (filled arrays)
But recently, I was tinkering with JavaScript in the Google Chrome Developer Console and came across this:
Now the second array is like arrayA, and the third like arrayB as shown in the console.
But the first array ([...'🏃🏽♀️'])... what is it?
I opened up its directory and saw the elements that were defined as hole were undefined with their respective keys in the array.
I also ran a few types of JavaScript loops on the array:
for...in statement captures all elements, except the *hole*s.
for...of statement captures all elements, except the *hole*s and proceeds to throw an error that the iterator variable used is undefined i.e.:
for (var value of [...'🏃🏽♀️']) console.log(value);
// Throw 'ReferenceError' when loop is done.
Array.prototype.forEach method captures all elements, except the *hole*s.
do...while, for and while statements captures all elements, except the *hole*s.
Why does the console see those values as different from empty or undefined (as with arrayA and arrayB)?
The main question is: Is there implicitly another type of array and if so, is there anything to note about it?
The ... is known as spread syntax. Read more about it here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
Emojis are made up of a variety of elements which the browser renders as a single emoji. Here's a quick article that expands on that. https://til.hashrocket.com/posts/2f488279a3-expand-emojis-with-the-spread-operator
By applying the spread syntax to an emoji, you can look at the individual emojis it's composed of.
I'm wondering if I can rely on the fact that [1,,2] only has two keys: 0 and 2, or if anyone happens to know if any JS engines will also give me a 1.
Every browser I've tested shows keys 0, 2, but I don't have older versions available at the moment, or an android phone, or ...
Reasoning:
I'm writing a custom timer library on top of requestAnimationFrame and so am returning cancelable ids based on internal array indices. I'm trying to figure out if simply delete ary[ix] is sufficient to be able to walk all of the object keys on the array without extra sanity checks.
You are covered if you are willing to assume that your code will be running on a conforming implementation of ECMAScript. From the spec:
Array elements may be elided at the beginning, middle or end of the
element list. Whenever a comma in the element list is not preceded
by an AssignmentExpression (i.e., a comma at the beginning or after
another comma), the missing array element contributes to the length of
the Array and increases the index of subsequent elements. Elided array
elements are not defined. If an element is elided at the end of an
array, that element does not contribute to the length of the Array.
[1,,3] will produce
[1, undefined, 3]
The length returned is 3.
Edit: to clarify, even though the value is present, a key is not returned.
Object.keys([1,,3]) returns ["0", "2"]
Edit 2: a great answer about checking if an array key exists.
Checking if a key exists in a JavaScript object?
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 situation. 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.