This question already has answers here:
Is Chrome’s JavaScript console lazy about evaluating objects?
(7 answers)
Closed 3 years ago.
I have an object with an array (of class objects) as one of its values. I have a function that, in part, runs a class method on one of the objects inside that array (within the object).
When I run my code, printing the array before and after the function, the change is both present before AND after the function runs.
Why is this happening? Hoisting?
As a test, I created another key:value pair in the object such that the value is an integer, and changed my function to just bump that integer up 1. Here, it works fine - the print of my object before the function has that integer as 1, and then afterward has the integer as 2.
I also tried NOT using a class method on the object to make the adjustment, and it still failed.
class Book{
constructor (color, title, pagecount){
this.color = color;
this.title = title;
this.pagecount = pagecount;
}
changePages() {
this.pagecount += 50;
}
}
let book1 = new Book("Red", "Book1", 100);
let book2 = new Book("Blue", "Book2", 200);
let book3 = new Book("Green", "Book3", 300);
var myBookArr = [book1, book2, book3]
var myObj = {arr: myBookArr, integerTest: 0}
function thisDoesStuff(){
//other operations not related to myObj
myObj.arr[0].changePages();
}
When I run the below, in BOTH console.logs, it shows that arr[0] (which is book1) has 150 pages.
console.log(myObj);
changePages();
console.log(myObj);
I am expecting the first console.log to show book1 as its original value, then the function changes it.
Please, hover on the icon with i letter on it near your log.
You will see, that Chrome (if you using Chrome, of course) will say:
Value below was evaluated just now
It happens because objects and other complex entities are being passed by reference, not by value. So, when you are expanding you log, browser getting up-to-date value of the reference.
Try to console.log copy of your value (e.g. console.log({...myObj})), or use JSON.stringify or other string-like representation of your object.
Note, that it is not error in your code. It is just a feature of the console.log, so (if I interpreted it correctly) your code works just fine :)
Your code is correct, it's all based off when the browser decides to evaluate the object for console.log output.
See post here that another user was having a similar question:
Weird behavior with objects & console.log
Related
This question already has answers here:
Array.prototype.fill() with object passes reference and not new instance
(7 answers)
Closed 4 years ago.
I have a initial state like this:
constructor(props) {
super(props);
this.state = this.getInitialState();
this.setSqValue = this.setSqValue.bind(this);
}
getInitialState() {
return {
allSq: new Array(3).fill(new Array(3).fill(null))
};
}
Then in a method like setSqValue (called by clicking a button) when I assign a new value to a cell, strangely all other cells with any position in first dimensional and same position in second dimensional will take that value!
The second problem is that the state is changed while I didn't call any setState!
setSqValue(i, j) {
console.log(i, j);
let {allSq} = this.state;
let value = 'foo';
console.log('before', allSq);
allSq[i][j] = value;
console.log('after', allSq);
// this.setState({allSq});
}
The output in the console is this :
You can see the before and after values are same (why?!).
The state in the React developer tool:
In the chrome dev tools, objects aren't evaluated until you open them. Hover on the little icon to the right and you will see Object below was evaluated just now. . Try putting a debugger in there and I have a feeling you will see that your allSq in the before doesn't have foo in there. It seems as if everything is working correctly, you're just looking at the wrong things to see that.
Also, as another user said, .fill uses reference instead of value, so use .map instead.
In JavaScript, Arrays are a special kind of Object, and if you pass an object to fill(), it uses the same object each time. You should use map() instead, which will create a copy of the array.
allSq: Array(3).fill().map(() => Array(3).fill(null))
To prevent the state from being changed you can use the Object.freeze() method:
allSq: Object.freeze(Array(3).fill().map(() => Object.freeze(Array(3).fill(null))))
With this, the line allSq[i][j] = value; will ether throw an error (in strict mode) or silently fail (outside of strict mode).
This question already has answers here:
Copy array by value
(39 answers)
Closed 4 years ago.
I found a really weird (for me) problem
I have this global variable ARRAY
var A = [1,2,3,4]
then inside a function, I made local var and assign previous global var to it
function someFunc() {
var X = A;
}
I then made another local Var and assign it with the first local var's value
var Y = X;
I then push a new value to Y
Y.push(6);
but the, the new value (6) didn't only pushed to Y, but also to the 2 original array (X and A). What happened? Doesn't it supposed to only change Y?
Please help, thank you.
Here is my full code:
var A = [1,2,3,4];
function someFunc(){
var X = A;
var Y = X;
Y.push(6);
console.log(A);
console.log(X);
console.log(Y);
}
$("#test").click(function(){
someFunc();
});
as you can see, it is triggered by clicking on element with id #test.
All three console.log, even thought represent different variable, it return the same result, ARRAY with 6 on it
Edit. Yes there is a similar question to this, but even though the problem is similar and the solution is identical, but the initial understanding is what different. In my question, I initially asked "Why", because I am not aware of those variable actually 'refer' to same array instead of 'assigning', so I have no idea if I should search for how to assign instead of refer, since I assumed that using = means assigning, sorry
What happens is that you do not copy the array you just reference it. You can use the method slice to create a copy of it:
var X = A.slice();
Do mind that you can use this approach only with primitive values. Check this if you need to deal with objects How do you clone an Array of Objects in Javascript?
Your arrays aren't cloned: assigning a variable does not clone the underlying object.
You can shallow-clone an array using the slice method:
var cloned = original.slice();
But array and object items within the array are not cloned using this method. Numbers and strings are cloned, however, so this should work fine for your case.
An array in JavaScript is also an object and variables only hold a reference to an object, not the object itself. Thus both variables have a reference to the same object.
I have come across an interesting piece of code:
function repeat(str,x) {
return Array(x+1).join(str);
}
repeat("wow", 2);
The outcome of this is a string "wowwow". However, I have no idea what this Array(x+1) is actually doing. And very interesting thing is that if I just use Array(x) it prints the str only once and not twice as expected.
When I console.log Array(x+1) it gives this strange output:
Array(x+1) (3) [empty × 3]
I am aware that there exists a repeat() method on strings which can be used happily to achieve the same result as the presented function. But as I've come across it, I would like to know the mechanism behind Array(x+1). I also know what an array or new Array() is. But this I see for the first time.
Array is specified such that new is optional. From the spec:
When called as a constructor it creates and initializes a new Array exotic object. When Array is called as a function rather than as a constructor, it also creates and initializes a new Array object. Thus the function call Array(…) is equivalent to the object creation expression new Array(…) with the same arguments.
(my emphasis)
To my surprise, the MDN page is not at all clear about this. If I get time, I may have to fix that...
As you can see in console Array(x+1) creates array with 3 empty elements (as far as x = 2). Then you join these elements with string wow so you have:
empty element + "wow" + empty element + "wow" + empty element = "wowwow"
Array(x+1) creates an array of 3 elements all containing empty elements.
Later you are joining these empty elements with the string wow, thus returning:
empty + "wow" + empty + "wow" + empty => wowwow
It is a function that creates an array.
Things like this you should really just try in you console before asking.
These things can be done in Javascript even though they are somewhat contra intuitive:
function A() {
return new Array();
}
let myA1 = A(); // returns an array
let myA2 = new A(); // also returns an array.
EDIT:
The reason is that Javascript is a prototypal language and a "Class" is just a function that we new. If that function is not returning anything we get a new instance of in our case A and if the function returns some other object, that object gets returned.
What is does is to create an array with size x+1(3, in this example). And filling it each cell with the str variable value.
In this code I create an array of size 3 cells and fill them with the string "yolo".
$(document).ready(function(){
var str = "yolo";
console.log(Array(4).join(str));
})
Example fiddle: https://jsfiddle.net/6o90fv9c/
This question already has an answer here:
Javascript Console Log reporting object properties incorrectly
(1 answer)
Closed 5 years ago.
var obj = {name:"小明"};
console.log("obj1",obj);
// 1. {name:"小明",age:10}
obj.age = 10;
console.log("obj2",obj);
// 2. {name:"小明",age:10}
why does the browser console display the result at the first? I think the reuslt should be an unchanged object {name:"小明"}.Now ,I have a headache about it . appreciate your response.
This is because you are seeing/expanding the object after adding the new property age.
Initially the object has only one property but by the time you are inspecting it , a new key will be added to the object
Another way is to verify using hasOwnProperty. In below snippet the first console.log statement won't be executed since the object does not have the key age
var obj = {
name: "小明"
};
if (obj.hasOwnProperty('age')) {
console.log("obj1", obj);
}
obj.age = 10;
console.log("obj2", obj);
This depends on the browser. If I recall correctly, chrome logs an unchanging object whereas firefox logs a changing one. Either way, obj.age will be undefined until you define it.
When you expand the object the values displayed in the console are evaluated at runtime.
Tooltip in Chrome console explaining that values are evaluated at runtime:
You could try JSON.stringify to print the current value to the console.
e.g.
console.log("obj1", JSON.stringify(obj));
This question already has answers here:
Is JavaScript a pass-by-reference or pass-by-value language?
(33 answers)
Closed 7 years ago.
I was curious, so I tried this out:
var test = example = 5;
Now, this is a somewhat common way of defining things...
(for instance in node, module.exports = exports = ...)
The above be equivalent to:
example = 5;
var test = example;
Now, if variables don't actually hold a value, but are rather references to values in the memory, when I change example, if test is referencing example which is referencing a value, shouldn't test also have that same value as example? Lets try it:
var test = example = 5;
example = 7;
console.log(test, example);
... Now, the output isn't really what I expected, due to what I put above.. You get this:
5 7
But how is test still 5 if test is referencing example, and example is now 7? Shouldn't they both be 7?
EDIT Tried using Objects instead... But I get the same behavior:
var test = example = {"hello":"world", "foo":"bar"};
example = {"test":"example"};
console.log(test, example);
This outputs:
Object {hello: "world", foo: "bar"}
Object {test: "example"}
So, they're still not the same like I expected.
var a = {}; // There is one object in memory
var b = a; // now both `b` and `a` refer to that object
a = {}; // now we create another object and put a reference to it to an `a`
// we haven't changed `b` so it refers to the former object
And to summarize: in JS you cannot change the value of another variable. Value of variables a and b in the example above is a reference, not the object itself. And the only way to change the value of the variable - is to re-assign another value to it. So you cannot change the value of a variable indirectly in JS, at all (?).
When speaking about java script, variables can accept multiply values. JavaScript is a very 'forgiving' language let's call it like this.
So, if you said for example:
var test = example = 5;
test would still be 5, because test is now given the value of 5. test is but a reference in this case to example.
the actual value of example, is 7.
example = 7;
So yes, it's a bit odd, But javascript behaves a bit differently then other programming languages.