javascript's object property member change so strangely [duplicate] - javascript

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));

Related

Why is this object changing before the function is run? [duplicate]

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

Change a 2d array in React state [duplicate]

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).

What is falsy here ? Why does it dedupe? and why does console.log() contain the answer when infront of the foreach?

Q1-Why does this message print with the object even though it is before the forEach?
Q2-Why does this dedupe the new object
Q3-What triggers the falsy here that allow the {} to be added?
var test = [{id:1,name:'s1'},{id:2,name:'s2'},{id:2,name:'s2'},{id:1,name:'s1'},{id:4,name:'s4'}];
var test1 = {};
//Q1
console.log(test1);
test.forEach(function(item){
//Q2 and Q3
var o = test1[item.name] = test1[item.name] || {};
o.id = item.id;
});
<!--Output
{}
​
s1: Object { id: 1 }
​
s2: Object { id: 2 }
​
s4: Object { id: 4 }
​
__proto__: Object { … }
--!>
The console is tricky. It does NOT lie, it just stores a reference to that object. It's not "taking a picture" of the object as it is when you log it, it's actually reflecting the object itself. If you were to update it on click, it would update that reference in the console, too. If you alert it, you would get [object Object] instead of the text. You can use console.log(JSON.stringify(test1)) to print the contents as they were at the moment the object was read in the code.
As for deduping, it's pretty straight-forward, but a bit of a logic puzzle. During one iteration, for example, it will see test1['s1'] and assign it to 's1'. The next time it runs across another s1, it's actually referencing the same property as before, test1['s1'] and reassigning it to 's1' again.
I'll come back to take a closer look at question 3, but it's a good idea to ask one clear question at a time here on StackOverflow. ;)
--
Edit: Well, I'm not exactly sure about how you're getting those values in your log to be honest, because I'm not getting the same results even though I'm running the same script. Check my code on codepen to see my script working as expected!
Q1. As discussed in comments, major browsers now log an inspectable tree of an object, not the conversion of the object to a string using its toString method. The tree is based on a reference to the object and may show property values current when expanding the tree rather than when the object was logged.
Q2. The forEach loop sets properties of test1 using item.name values for items in the test array. If name values are repeated in test entries, only the one property name is updated in test1. Hence the de-duplication - an object can't store separate properties of the same name.
Q3. Initialisation of a test1 property to an empty object only occurs the first time a property is created in test1 for a property name held in item.name. Subsequently, if the same value of item.name is encountered again, it retrieves the pre-existing property from test1 because
test1[item.name] || {};
now evaluates to the existing test1 object property. (If the left-hand operand of an || operator is non falsey, it returns the left-hand operator value as the result of the operation.)
That perhaps leaves the o variable - it is a copy of the reference to an object stored in test1. Updating its id property updates the same object as held in test1. If test had multiple entries of the same name but different id values, the test1 property for the name would hold the id of the last duplicate entry in test.

weird array behaviour in javascript [duplicate]

This question already has answers here:
Is Chrome’s JavaScript console lazy about evaluating objects?
(7 answers)
Closed 1 year ago.
I know arrays in javascript are a bit special, compared to other languages, but I don't really get this behaviour, or what's going on here.
I'd like to know why it happens and why I don't get an empty array:
function setupWindowProgBar(settings, window, palette, arr){
console.log('start');
console.log(arr);
if(typeof arr == 'undefined'){
var arr = [];
}
console.log(arr);
console.log('stop');
var arrLen = arr.length;
arr[arr.length] = createProgBar('master', 'bg', window, 0, settings.fillColor, settings.strokeColor, settings.textColor, palette, 'percent', settings.reqType, settings.sourceType, settings.sourceTarget, settings.sourceId);
return arr;
}
produces this in the console:
start
undefined
[]
0:
barType:"master"
bgcolor:"#12181f"
curVal:160
data:
all_goals:160
cost_hours:160
cost_hours_spent:0
cost_money:31610
cost_money_owned:0
parentObj:"progBar"
progress_goals:5
recurring:"no"
wanted_timer:"2018-03-26 05:19:33"
__proto__:Object
fill:"#255f6f"
height:59
maxVal:5
maxWidth:168
sectionHeight:59
stroke:"#7b9dac"
text:"3%"
textColor:"#dee5ed"
textOpt:"percent"
width:200
x:33
y:81
__proto__:Object
height:100
text:"omanko"
length:1
__proto__:Array(0)
stop
I do reckognize the objects in here, but it's not from global pollution as far as I can tell - console.log(window.arr) says that there are no global variables named arr, and I haven't modified the prototype.
Surely, that shouldn't effect a new array declaration anyway?
This behaviour isn't limited to arrays, any object behaves this way in the console
What you are seeing is the result of console in all browsers "lying" to you
if you console.log(anyobject) and inspect that object in the console, what you will see is current anyobject - not what it was when console.log was executed
var obj = {}
console.log(obj);
obj.test = 1;
var arr = [1];
console.log(arr);
arr.push(2);
Now, if you open the developer console, click on the Object, you'll see test:1
Look at the array in the console - it is output as [1] ... yet, click on the array you see both elements
Note: chrome developer console does at least hint at the fact that it's lying to you - there's a blue i, if you hover (or click, can't recall, don't use Chrome often enough) you'll see a message saying that the value shown is evaluated just now
The issue is when you call console.log(arr) it puts a reference to the array in the console that gets dereferenced when you "open" it (click on the arrow) so it will display changes that are made later in code. Use console.table(arr) or console.log(JSON.stringify(arr)) instead.
Here is a snippet from MDN
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 click it open.
arr is a parameter to your function already, and it is not undefined. var gets hoisted, so to the interpreter, your current code actually looks something like:
function setupWindowProgBar(settings, window, palette, arr){
var arr; // **does not reassign the parameter**
console.log('start');
console.log(arr);
if(typeof arr == 'undefined'){
arr = [];
}
console.log(arr);
console.log('stop');
}
You should never be declaring a variable whose name is already scoped to the block/function you're using it in.
If arr actually isn't defined in the argument, then it will be assigned to an array on the line arr = [];, but at least on Chrome, that empty array will not necessarily be printed at the time you console.log it.
console.log(arr); does not necessarily immediately display the arr - depending on your browser, it may only be populated in your console after you open your console, after this line has run:
arr[arr.length] = createProgBar('master', 'bg', window, ...

Why do people declare something as null in JavaScript? [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Why is there a null value in JavaScript?
I don't understand what null is for. 'Undefined' as well.
It helps to appreciate the difference between a value and a variable.
A variable is a storage location that contains a value.
null means that the storage location does not contain anything (but null itself is just a special kind of value). Read that last sentence carefully. There is a difference between what null is and what null means. 99% of the time you only care about what null means.
Undefined means that the variable (as opposed to the value) does not exist.
null is an object you can use when creating variables when you don't have a value yet:
var myVal = null;
When you do this, you can also use it to check to see if it's defined:
// null is a falsy value
if(!myVal) {
myVal = 'some value';
}
I wouldn't use it this way, normally. It's simple enough to use 'undefined', instead:
var myVal; // this is undefined
And it still works as a falsy value.
Creating Objects
When you create an object in javascript, I like to declare all my object properties at the top of my object function:
function myObject() {
// declare public object properties
this.myProp = null;
this.myProp2 = null;
this.init = function() {
// ... instantiate all object properties
};
this.init();
}
I do this because it's easier to see what the object properties are right off the bat.
If a variable is not known or not initialized in the current scope it is undefined.
If you want to use a variable and indicate that it has an invalid value for instance you may want to assign it null because accessing an undefined variable will throw an error if you try to work with it (despite checking if it is undefined).
If something is undefined you also have the possibility to add it to an object. If a variable is defined but contains null you know that this variable may be used already by another part of your program.
For example, it can prepare a global variable to be used in more parts of the code.
If you start your code with var iNeedThisEverywhere = null; then you can use it inside more objects/actions in the code, but if you don't do that, then this variable will not be shared throughout the code.
Of course, you will actually put something inside that variable somewhere during the code, but since you defined this variable at the very beginning, it will be shared further anyway.
A variable holding null holds a reference to the "black hole" named null.
10 + 10 + null = null
Null is related historically to databases, http://en.wikipedia.org/wiki/Null_(SQL)
(Correct me if I'm wrong here)
A variable not initiated (unknown) is undefined.
regards,
/t

Categories

Resources