How do object references work internally in javascript - javascript

I am very new to javascript.
I have written the simple code:
var temp = {}
var arr = []
temp['a'] = ['a']
arr.push(temp)
console.log(arr);
As expected, it prints:
[ { a: [ 'a' ] } ]
But then, when I append the following line to the previous code:
temp['b'] = ['b']
arr.push(temp);
console.log(arr);
I would have expected it to print:
[ { a: [ 'a' ] }, { a: [ 'a' ], b: [ 'b' ] } ]
But it prints:
[ { a: [ 'a' ], b: [ 'b' ] }, { a: [ 'a' ], b: [ 'b' ] } ]
Entire code for unexpected result:
var temp = {}
var arr = []
temp['a'] = ['a']
arr.push(temp)
console.log(arr);
temp['b'] = ['b']
arr.push(temp);
console.log(arr);
Why did the first element of array got updated?
The following code gave me expected result:
var temp = {}
var arr = []
temp['a'] = ['a']
arr.push(temp)
console.log(arr);
temp = {};
temp['a'] = ['a']
temp['b'] = ['b']
arr.push(temp);
console.log(arr);
How does adding temp = {} helped here?

Objects in Javascript are passed by reference. That is, only one object is created and the symbol that represents that object can be used but it will refer to the same object always.
Lets take a deeper look:
If I'm understanding your example correct, this part
var temp = {}
var arr = []
temp['a'] = ['a']
arr.push(temp)
console.log(arr);
Creates a local variable temp to which you add ['a'] to. You then push that into arr.
So at this point, arr references the object temp and looks like this:
[ { a: [ 'a' ] } ]
When you do this:
temp['b'] = ['b']
arr.push(temp);
console.log(arr);
The temp symbol which points to the original object containing ['a'] is updated, and so the arr will also get updated, so arr contains this at that point:
[ { a: [ 'a' ], b: [ 'b' ] }, { a: [ 'a' ], b: [ 'b' ] } ]
Finally,
You then do this instead:
temp = {};
temp['a'] = ['a']
temp['b'] = ['b']
arr.push(temp);
console.log(arr);
This creates a separate global variable temp, onto which you add both
['a'] and ['b']. This is global because it does not have the var keyword in the declaration/initialization. This then gets pushed into the arr. However, since it's a global variable and not the original local variable, you see this instead:
[ { a: [ 'a' ] }, { a: [ 'a' ], b: [ 'b' ] } ]

In first case, arr[0] has temp's reference, arr[1] also has temp's reference. So, arr[0] and arr[1] have the same reference.
Hence updating the reference will update it everywhere where the reference is
being referred.
In second case however, when you do temp = {} you're just reassigning temp to a new reference, before pushing it. So, there's no relationship between the arr[0]'s reference, and hence updating temp now, only affects it.

The examples are not the same, it doesn't have to do with temp = {}.
In the first example you push temp twice, meaning arr has to references 2 temp.
After the first push you add another item to temp so within arr, if you had print it, you would have seen:
[ { a: [ 'a' ], b: [ 'b' ] } ]
So try this out on the console:
var temp = {}
var arr = []
temp['a'] = ['a']
arr.push(temp)
temp['b'] = ['b']
console.log(arr);
You'll see the result:
[ { a: [ 'a' ], b: [ 'b' ] } ]
Pushing another temp into arr is just going to result into two references into temp.

There are two data types in JavaScript - value types and reference types.
Value types are actually copied as they are sent between objects. This is because this is what you would expect for certain things like numbers and booleans.
Say I pass the number 1 to a function that stores it in an object A.
It would be surprising if I could then subsequently modify the value contained in A simply by modifying the value of the original number. Hence, pass by value. There are also optimizations that can be performed for value types.
Objects (i.e. everything other than number literals, boolean literals, null, undefined, and string literals*) are reference types in JavaScript and only their reference is passed around. This is largely for efficiency reasons. In your example, temp is an object. It is therefore passed by reference.
And so
temp['b'] = ['b']
Modifies the single existing instance of temp, thereby modifying the contents of arr, before you then also push temp into arr for a second time.
So you end up with an array containing two references to a single object temp, giving you the observed result.
* There is some complexity surrounding the string implementation that I am purposefully ignoring here.

Related

Potentioal memory leak with spread operator?

I'm just wondering if the following code snippet is a potential memory leak.
let arrOfObj = [
{ a: 0, b: 0 }
]
const copy = [ ...arrOfObj ]
copy[0].a = 5;
console.log(arrOfObj)
// [ { a: 5, b: 0 } ]
console.log(copy)
// [ { a: 5, b: 0 } ]
arrOfObj = []
console.log(arrOfObj)
// []
console.log(copy)
// [ { a: 5, b: 0 } ]
The spread operator will only do a shallow copy, so the object inside the array would be a reference. But where did javascript get the values in the last log? Will they garbage collected as I empty the array?
No, there is no memory leak. Breaking down the steps for better explanation:
const copy = [...arrOfObj] : allocates space in memory heap and assign it to copy variable. Since, spread performs shallow copy, the object reference is still same inside of the array.
copy[0].a = 5; : updates the value of the object inside of copy array. Since, the reference to object is same for both copy and arrOfObj, it is reflected in both the arrays.
arrOfObj = [] : allocates space in memory heap for [] and assigns it to arrOfObj variable. This does not imply that copy loses its reference to the array you created earlier. copy still points to the older memory reference. Hence, copy prints out the older array containing object, and arrOfObj prints empty array.
To ellaborate more on this, even if you do the following, copy would still point to older memory reference :
let arrOfObj = [
{ a: 0, b: 0 }
]
// instead of spread, assign it directly
const copy = arrOfObj;
copy[0].a = 5;
console.log(arrOfObj)
// [ { a: 5, b: 0 } ]
console.log(copy)
// [ { a: 5, b: 0 } ]
// assign empty array to 'arrOfObj'
arrOfObj = []
console.log(arrOfObj)
// []
console.log(copy)
// [ { a: 5, b: 0 } ] => still prints the array
Also, the older value of arrOfObj "may" get garbage collected (depends on JS engine optimisations)

How do you push a variable into an array multiple times without it changing?

I'm trying to push some values in an array for something called "Brain.js". When storing a variable in the array and later changing it, all the variables that were stored in the array change. Can someone help me make it so they don't change? I'm having much trouble with this.
Here's an example:
var hold = ([
]);
var a = [1, 1, 1]
var b = [2];
hold.push(
{ input: a, output: b }
);
console.log(hold); // returns [ { input: [ 1, 1, 1 ], output: [ 2 ] } ]
a[2] = 2;
b = [3];
hold.push(
{ input: a, output: b }
);
console.log(hold);
// Expected output: [ { input: [ 1, 1, 1 ], output: [ 2 ] }, { input: [ 1, 1, 2 ], output: [ 3 ] } ]
// What it really returns: [ { input: [ 1, 1, 2 ], output: [ 2 ] }, { input: [ 1, 1, 2 ], output: [ 3 ] } ]
Problem is, that you are not pushing actual number into the array, but reference. In other words, you passed twice the reference to same object.
What could you do, is create a copy of the object when you are passing it to hold. You can use eg. spread operator.
hold.push(
{
input: ...a,
output: ...b
}
);
You can find out more here
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
Problem is you are updating an existing array a which is already referenced inside first object you pushed. You should create a copy of an existing array if you do not wish to modify it.
var hold = ([
]);
var a = [1, 1, 1]
var b = [2];
hold.push({
input: a,
output: b
});
console.log(hold);
a = [...a]; // create a new copy of a
a[2] = 2;
b = [3];
hold.push({
input: a,
output: b
});
console.log(hold);

What differences between these two ways of object declaration?

I was just wondering if there are any differences between these ways of JSON object declaration or they do the same thing? What is the standard (recommended) way to declare an object?
According to my test, they both give the same result.
let data1 = {
"record_1": [1,2,3],
"record_2": [4,5,6]
}
let data2 = {
record_1: [1,2,3],
record_2: [4,5,6]
}
console.log(data1);
console.log(data2);
console.log(data1.record_1);
console.log(data2.record_1);
console.log(data1.record_2);
console.log(data2.record_2);
console.log(JSON.stringify(data1));
console.log(JSON.stringify(data2));
Output:
{
record_1:(3) [...],
record_2:(3) [...]
}
{
record_1:(3) [...],
record_2:(3) [...]
}
(3) [
1,
2,
3
]
(3) [
1,
2,
3
]
(3) [
4,
5,
6
]
(3) [
4,
5,
6
]
{"record_1":[1,2,3],"record_2":[4,5,6]}
{"record_1":[1,2,3],"record_2":[4,5,6]}
Both of the declarations are valid in Javascript
let data1 = {
"record_1": [1,2,3],
"record_2": [4,5,6]
}
let data2 = {
record_1: [1,2,3],
record_2: [4,5,6]
}
but when it comes to JSON , data2 is invalid JSON syntax. You can verify at https://jsonlint.com/
One more diff is as below:
var obj = { "some key" : "Val" }; // Valid in JS
var obj = { some key : "Val" }; // invalid in JS
So for JS , both of these deceleration plays different role depending on the situation. Normally, data2 type declaration is widely used.
Object's property name are of type string, if you provide any other type then that is automatically converted to string.
var obj = {1: "one"}
var keyName = Object.keys(obj)[0];
console.log(`key Name ${keyName} and type is ${typeof keyName}`);
I will prefer the explicit way (using the quotes) of declaration as this will reduced the confusion (from the reader of the code).
They're essentially the same. One difference is that when you use quotes, you can use special characters as key.
// invalid
const noQuotes = {
key with spaces: 123
}
// valid
const withQuotes = {
"key with spaces": 123
}

Deep key structure based on recursion

I've been using lodash for a while now and I really love the _.set and _.get methods.
I'm trying to solve a problem to get the deep key paths whose final value is a string, but when I'm too dumb for it. Spend 3 hours on it and can't find a perfect solution:
const myObject = {
a: 'myObject.a',
b: {
ba: 'myObject.b.ba',
bb: ['myObject.b.bb[0]'],
},
c: [
{ ca: 'myObject.c[0].ca' },
],
};
So I have myObject (that's far more nested in real life) and I want to get paths to the values, but only the final one.
The method would look like getDeepPaths(myObject) and would return in this case: ['myObject.a', 'myObject.b.ba', 'myObject.b.bb[0]', 'myObject.c[0].ca' ]
Did anyone solve something like this before?
Recursion is actually not that hard. Here's how you could solve this problem:
const myObject = {
a: 'myObject.a',
b: {
ba: 'myObject.b.ba',
bb: ['myObject.b.bb[0]'],
},
c: [
{ ca: 'myObject.c[0].ca' },
],
};
var stringLeaves = function(path, obj) {
if (typeof obj === 'string') {
return [path]
}
return Object.keys(obj)
.filter(k => obj.hasOwnProperty(k))
.map(k => stringLeaves(path + '.' + k, obj[k]))
.reduce((a,x) => a.concat(x), []); // this line flattens the array
};
console.log(stringLeaves('myObject', myObject));
The work is done by the stringLeaves function. In this function:
if the obj passed in as a parameter is a string, then just return the current path.
otherwise we assume that the object is an array, or a generic object, in which case we iterate through its properties:
for each property, call stringLeaves recursively, by passing in the adjusted path (current path + the new property name) and the object/value that resides at that particular key.
The convention of the function is that it returns an array of all possible matches. This is why:
for scalar string values I return an array (to keep things consistent)
I have the .reduce((a,x) => a.concat(x), []); line: to transform an array of arrays into one array that consists of all the values present in the original arrays.
Note that the function cannot deduce that your object is called myObject, so I passed that name as an initial path.
I'll provide a more generic solution that doesn't use lodash or other external dependencies
const traverse = function* (node, path = [])
{
if (Object (node) === node)
for (const [ key, value ] of Object.entries (node))
yield* traverse (value, [ ...path, key ])
else
yield [ path, node ]
}
We can easily step thru our data using a for loop. Notice the generator yields a path-value pair for each value in the original object. All primitive values are included in the output, not just strings this time
// add a non-string value for demo
const myObject = {
...
d: 1
};
for (const [ path, value ] of traverse (myObject)) {
console.log ('path', path)
console.log ('value', value)
console.log ('---')
}
// path [ 'a' ]
// value myObject.a
// ---
// path [ 'b', 'ba' ]
// value myObject.b.ba
// ---
// path [ 'b', 'bb', '0' ]
// value myObject.b.bb[0]
// ---
// path [ 'c', '0', 'ca' ]
// value myObject.c[0].ca
// ---
// path [ 'd' ]
// value 1
// ---
If we wanted to, we can collect all of the pairs using Array.from
Array.from (traverse (myObject))
// [ [ [ 'a' ], 'myObject.a' ]
// , [ [ 'b', 'ba' ], 'myObject.b.ba' ]
// , [ [ 'b', 'bb', '0' ], 'myObject.b.bb[0]' ]
// , [ [ 'c', '0', 'ca' ], 'myObject.c[0].ca' ]
// , [ [ 'd' ], 1 ]
// ]
As you may have noticed, I keep path as an array rather than making it a .-separated string. There's no need to make it into a string just to split it apart again later.
const lookup = (obj, [ key, ...path ]) =>
obj && key
? lookup (obj [key], path)
: obj
for (const [ path, value ] of traverse (myObject)) {
console.log ('path', path)
console.log ('value', value)
console.log ('lookup', lookup (myObject, path))
console.log ('---')
}
// path [ 'a' ]
// value myObject.a
// lookup myObject.a
// ---
// path [ 'b', 'ba' ]
// value myObject.b.ba
// lookup myObject.b.ba
// ---
// path [ 'b', 'bb', '0' ]
// value myObject.b.bb[0]
// lookup myObject.b.bb[0]
// ---
// path [ 'c', '0', 'ca' ]
// value myObject.c[0].ca
// lookup myObject.c[0].ca
// ---
// path [ 'd' ]
// value 1
// lookup 1
// ---

Removing Duplicates in Arrays

I am trying to pass a function that removes duplicates from an array. It should handle strings, object, integers as well. In my code so far I am showing that it will handle strings but nothing else. How can Imake this function universalto handle numbers,handle arrays,handle objects, and mixed types?
let unique = (a) => a.filter((el, i ,self) => self.indexOf(el) ===i);
In this function I hav unique() filtering to make a new array which checks the element and index in the array to check if duplicate. Any help would be appreciated.
i think the first you should do is to sort the array ( input to the function ). Sorting it makes all the array element to be ordered properly. for example if you have in an array [ 1, 3, 4, 'a', 'c', 'a'], sorting this will result to [ 1 , 3 , 4, 'a', 'a' , 'c' ], the next thing is to filter the returned array.
const unique = a => {
if ( ! Array.isArray(a) )
throw new Error(`${a} is not an array`);
let val = a.sort().filter( (value, idx, array) =>
array[++idx] != value
)
return val;
}
let array = [ 1 , 5, 3, 2, "d", "q", "b" , "d" ];
unique(array); // [1, 2, 3, 5, "b", "d", "q"]
let obj = { foo: "bar" };
let arraySize = array.length;
array[arraySize] = obj;
array[arraySize++] = "foo";
array[arraySize++] = "baz";
array[arraySize++] = obj;
unique(array); // [1, 2, 3, 5, {…}, "b", "baz", "d", "foo", "hi", "q"]
it also works for all types, but if you pass in an array literal with arrays or objects as one of its element this code will fail
unique( [ "a", 1 , 3 , "a", 3 , 3, { foo: "baz" }, { foo: "baz" } ] ); // it will not remove the duplicate of { foo: "baz" } , because they both have a different memory address
and you should also note that this code does not return the array in the same order it was passed in , this is as a result of the sort array method
Try using sets without generics. You can write a function as
Set returnUnique(Object array[]) {
Set set=new HashSet();
for (Object obj:array) {
set.add(obj);
}
return set;
}

Categories