Javascript objects and arrays references behaving different deppending on the depth - javascript

Just trying to understand why this is happening.
I was experimenting a bit with javascript arrays, objects and references to the objects. Then I reached a point where I don't understand anymore.
I create an array that holds an object; then when I do a reference to that object and change a property; the values of the original and the reference are changed automatically. Obviously because it's a reference and not a copy.
I was expecting the same when I create an array with an object that holds and array with one other object but somehow in that case the original object remains untouched.
Probably It has a really simple explanation that I don't know right now. Someone could explain me that?
Thanks!
var foo = [
{
"bar": "bar"
}
];
var ref = foo[0];
ref.bar = "new bar";
console.info(foo[0].bar); // output: new bar
console.info(ref.bar); // output: new bar
console.info(foo[0].bar === ref.bar); // output: true
var foo2 = [
{
"bar2": [
{ "nested": "nested" }
]
}
];
var ref2 = foo2[0].bar2;
ref2.nested = "nested 2";
console.info(foo2[0].bar2[0].nested); // output: nested
console.info(ref2.nested); // output: nested 2
console.info(foo2[0].bar2[0].nested === ref2.nested); // output: false

Everything is going fine. Nothing has changed. you understood well.
It's just that you put foo2[0].bar2 in var ref2.
var ref2 = foo2[0].bar2;
you see foo2[0].bar2 is an array here. when you did
*ref2.nested = "nested 2";*
It added a new object to ref2.
what you want to do is
*var ref2 = foo2[0].bar2[0];*
Or anything else subtle.
Javascript is same friend. It's just us who lose reference.

Related

Why does modifying values of let x = arr.filter(...) alter the values of arr? [duplicate]

This question already has answers here:
Is JavaScript a pass-by-reference or pass-by-value language?
(33 answers)
Closed 4 years ago.
I am struggling to understand the behavior of the following lines of code:
// I'd like to keep this value constant
let allObjects = [{value: null}, {value: 'Hello World'}]
// Here is a shorter list of objects matching some criteria
let someObjects = allObjects.filter(object => object.value)
// Here we work with the the values for some of the objects
someObjects[0].value = {hmm: 'test'}
// Kind of expecting allObjects[1].value to be 'Hello World' at this point
// Display all the objects
console.log(allObjects)
And the output:
[
{
"value": null
},
{
"value": {
"hmm": "test"
}
}
]
Here is a codepen.
What I do not understand is when the value of someObjects is modified it affects the value of allObjects and expect that allObjects[1].value will return Hello World.
Could someone explain to me why this is actually happening and how we are supposed create a sorter version of the array that does not mutate the original array when it is modified?
In JavaScript all primitive variables are passed by value however all objects are passed by a copy of its reference More Info. In your case, the array contains objects. Each index in the array contains a simple pointer to the memory location of an object. When you filter the array you are creating a new array with fewer values however the pointers are still pointing to the same internal objects. The filtered array, someObjects, contains a single pointer that points to the object { value: 'Hello World' }. You are overwriting the value property of this object with another object, { hmm: 'test' }. If you instead wanted to replace the object in the new filtered array rather than changing the value property of the old object you would do someObjects[0] = { hmm: 'test' }
const obj = { foo: 'bar' };
const copy = obj;
console.log(obj);
copy.foo = 'changed';
console.log(obj);

How are the value of two objects linked when I link an object to an array of objects?

I have a tes test service with two objects shown here in my Typescript code:
test: ITestView;
tests: ITestView[];
This code checks each object in the tes.tests array and when an id matches it assigns an object in the array to equal another object tes.test:
tes.tests.forEach((test: ITestRow) => {
test.current = false;
if (test.id == id) {
tes.test = test; // << Linking them somehow here
test.current = true;
}
});
Later on I do this:
tes.test.current = false;
This code sets the value of tes.tests[0].current to false and tes.test.current to false.
When I now do this:
tes.test = null;
This code sets the value of tes.test to null but does not do anything to the tes.tests[] array.
Can someone explain why it does not effect the tes.tests[] array?
test = foo; // test references existing object foo
tests[0] = test; // test[0] also references existing object foo
test = null; // test doesn't reference anything, but tests[0] still does
Depending on what you expect, you could do:
tests[0] = null; // tests[0] doesn't reference anything
or:
tests.splice(0,1); // removed the 1st item from the tests
// not tests array became shorter!
I think the quick and simple answer is that
tes.test = null;
doesn't destroy the object it's pointing to. tes.test now points to nothing.
To clarify, tes.test.current = falsechanged both objects was because it referenced the current key of the object tes.test was pointing to.
tes.test = null;
The above code doesn't make any changes to tes.tests[] because it simple makes tes.test point to nothing instead of changing the object it is pointing to.
I hope I was clear!

Generic 2D hash in JavaScript?

In other languages it is possible to create a generic 2D hash. I know creating 2d hashes is possible in javascript as well as explained here, but I can't seem to find a generic way to achieve this.
As an example of what I am looking for. In Ruby you can do this:
2dhash = Hash.new{|h, k| h[k] = Hash.new }
puts 2dhash["test"]["yes"]
#=> nil
2dhash[1][2] = "hello"
puts 2dhash[1][2]
#=> "hello"
Notice that I have not initialized the second level of hash, it happens automatically.
Is it possible to somehow achieve the same in javascript? Specifically, a way to make a 2d hash without initializing the first level of hash (or hard-coding it to be even more specific). The 2dhash will be used dynamically, so I have no clue what the first level will be.
Looks like a nice data structure excercise, let me try :D
function Hash() {
this.hash = {};
}
Hash.prototype.set = function(val) {
var paths = Array.prototype.slice.call(arguments, 1) // all levels
var path = paths.shift() // first level
var hashed = this.hash[path]
if (paths.length) {
// still have deeper levels
if (!(hashed instanceof Hash)) {
hashed = this.hash[path] = new Hash()
}
Hash.prototype.set.apply(hashed, [val].concat(paths))
} else {
// last level
this.hash[path] = val
}
}
Hash.prototype.get = function() {
var paths = Array.prototype.slice.call(arguments, 0) // all levels
var path = paths.shift() // first level
var hashed = this.hash[path]
if (paths.length) {
// still have deeper levels
return Hash.prototype.get.apply(hashed, paths)
} else {
// last level
return hashed
}
}
Now, let's see if it works:
var trytry = new Hash()
trytry.set('the value to store', 'key1', 'key2')
trytry.get('key1') // Hash{key2: 'the value to store'}
trytry.get('key1', 'key2') // 'the value to store'
Hooray it works!
It also works for even deeper levels:
trytry.set('the value to store', 'key1', 'key2','key3', 'key4')
trytry.get('key1', 'key2','key3') // Hash{key4: 'the value to store'}
However, a disadvantage of this approach is that you have to use instance methods get and set, rather than native object literal getter/setter.
It's still incomplete. For production environment, we need to do more, e.g. methods and properties like contains, size, etc.
If you initialize the first level of the hash with objects, then you can reference the second level without typeErrors, even if the data was not defined before.
Example:
var _2dhash = {a: {}, b: {}, c:{}}
//Note you cannot start variable names with numbers in js
_2dhash['a']['missingElement'];
// > undefined
It works because you're accessing undefined properties of defined objects. If you try to access through a missing top-level object, ie.
_2dhash['d']['whatever'];
You will get a TypeError, because _2dhash.d was not defined, and the second lookup fails, trying to read the 'whatever' property of undefined.

Accessing plugin prototype function using array square [] brackets

I am very new to JS and I was just going through the syntax of modal.js. Basically I have a small difficulty, a lot of classical JS plugins use the below skeleton code for the plugin:
var Modal = function(element , options){
this.options = options
this.$body = $(document.body)
this.$element = $(element)
this.isShown = null
this.$backdrop =
this.scrollbarWidth = 0
}
Modal.prototype.toggle = function (_relatedTarget) {
// do something
}
Modal.prototype.show = function (_relatedTarget) {
// do something
}
var data = new Modal(somthing , radnom);
// now if we assume that option is "show",
//the show function in Modal will be executed
// but my question is data is not an array, so how can we use
// [] square brackets to access the properties of Modal/data ??
data[option](_relatedtarget);
Now my question is about accessing the properties of a plugin, see how a function is being called using the following syntax:
data[option](_relatedtarget);
See my comment in the code. How can we access the properties of data using []; it's not an array, right?
[] are not just for arrays
You can use [] to access properties on an object too.
You can use
data["show"] to access the show method
OR
data.show which is the same thing
One advantage of the [] is that you can use a variable within the brackets
var option = "show";
data[option](something); // call the `show` method on `data`
If you know the method you want to call, using the . is much nicer looking in the code
data.show(something); // much quicker (to type), and prettier
JavaScript has arrays:
var anArray = [ 1, 2, 3, 4 ];
and associative arrays (also known as maps):
var anAssociativeArray = { first: "No. 1", second: 2, somethingElse: "Other" };
both of these data structures can be accessed via []:
anArray[3] // will get the element of the array in position 3
// (starting counting frrom 0).
anAssociativeArray['first'] // will get the element of the associative array with the
// key 'first'.
Associative arrays can also be accessed via the .key notation:
anAssociativeArray.first // will also get the property with key 'first'.
The . notation can be used if you know the key you want to access but if you want to dynamically select which key then you need to use the [] notation.
var whichOptionToPick = 'somethingElse';
var value = anAssociativeArray[ whichOptionToPick ]; // will get the value "Other".

JavaScript array of pointers like in C++

I'm faced with a situation in JavaScript when I need to update an object via its pointer similar to ะก++ array of pointers to objects
Example code for my issue:
var foo = new Array();
var bar = function(){
this.test = 1;
foo.push(this); // push an object (or a copy of object?) but not pointer
};
var barInst = new bar(); // create new instance
// foo[0].test equals 1
barInst.test = 2;
// now barInst.test equals 2 but
// foo[0].test still equals 1 but 2 is needed
So, how can I solve this? Should I use a callback or something like this or there is an easy way to help me to avoid copying the object instead pushing the raw pointer into an array?
JS is pass-by-value, so your original assignment was this.test = the value of 1, in my example, it's this.test = the object pointed to by ptr, so when I change ptr this.test changes as well.
var foo = [],
ptr = {val: 1},
bar = function(){
this.test = ptr;
foo.push(this); // push an object (or a copy of object?) but not pointer
},
barInst = new bar(); // create new instance
// foo[0].test.val equals 1
ptr.val = 2;
// foo[0].test.val equals 2
Although if you thought that foo.push(this); was similar, it isn't. Since this is an object, the array will indeed contain "raw pointers" to objects, just like you want. You can prove this simply:
foo[0].test = 3;
// barInst.test === 3
Which shows that it is indeed a pointer to the object that was pushed onto the array
"create object method pointer"
Object.defineProperty(Object.prototype,'pointer',{
value:function(arr, val){
return eval(
"this['"+arr.join("']['")+"']"+
((val!==undefined)?("="+JSON.stringify(val)):"")
);
}
});
ex of use
var o={a:1,b:{b1:2,b2:3},c:[1,2,3]}, arr=['b','b2']
o.pointer(arr) // value 3
o.pointer(['c',0], "new_value" )

Categories

Resources