using array.shift() affects wrong variable - javascript

I'm assuming I'm doing something really dumb here. I basically have an array that I'm passing into a function. I want to remove an element of that array and do a bunch of stuff to it, and then iterate through the rest of the array as long as there are still members in that array. When the array is empty, I want to loop back and run through the original array again.
However, I'm running into a weird issue when using array.shift(). It seems to be affecting the wrong variable if that even makes sense.
Abstract example as follows:
var originalArray = [1,2,3,4,5,6,7,8,9]
function goThroughArray(array){
var untouchedArray = array
console.log('untouched array before is ' + untouchedArray)
var insideArray = untouchedArray
console.log('inside array is ' + insideArray)
var removedNumber = insideArray.shift()
console.log('removed number: ' + removedNumber + ' from insideArray')
console.log('inside array after is ' + insideArray)
console.log('untouched array after is ' + untouchedArray)
}
goThroughArray(originalArray)
The console log output yields:
untouched array before is 1,2,3,4,5,6,7,8,9
inside array is 1,2,3,4,5,6,7,8,9
removed number: 1 from insideArray
inside array after is 2,3,4,5,6,7,8,9
untouched array after is 2,3,4,5,6,7,8,9
This is without any looping going on. Can someone explain why doing the shift() on the insideArray ALSO affects the untouchedArray??
I would expect that the insideArray would lose it's first member which is stored as "removedNumber" but why is the untouchedArray also losing it's first member?
EDIT
function addOne(number){
console.log('number in is: '+number)
var original = number;
var modified = original
console.log('original before is ' + original)
console.log('modified before is ' + modified)
modified++
console.log('original after is ' + original)
console.log('modified after is ' + modified)
}
addOne(1)
Yields:
number in is: 1
original before is 1
modified before is 1
original after is 1
modified after is 2
NEW EDIT
Although this question is super old, I figured I would update with a much cleaner method to solve this question:
JSON.parse(JSON.stringify(obj))
will create a copy of the object.

In Javascript objects are never copied. If you write
var a = [1, 2, 3, 4];
var b = a;
both a and b are just pointers to the same array object. Thus for example executing a.push(99) you will see the new element when dumping b too.
It seems that copies are done with immutable types like numbers or strings:
var a = 14;
var b = a;
b = b + 3;
console.log(a); // still 14
but this happens because the + operator returns a new number object and b is bound to this new one instead of the old one.
If you need to make a copy you must do that explicitly. For arrays the solution is calling the slice method passing no parameters.
var b = a.slice(); // Makes a new independent copy of the array

In JS, assignments are done by value:
var a = 1,
b = a; // 1 (by value)
a = 2;
b; // 1 (not modified)
However, in the case of objects, that value is a reference in memory. That means that if you modify a property of the object, you will see the change in all variables:
var obj1 = {},
obj2 = obj1;
obj1.foo = 'bar';
obj2.foo; // 'bar' (the change is reflected in the other variable)
obj1 = {}; // However, if you replace the entire object, the change
// won't be reflected because assignments are done by value
obj1.foo; // undefined
obj2.foo; // 'bar'

javascript is pass by value language. read this link for more inforamtion or google it.
In this case, you should clone your array.
try this
let insideArray = untouchedArray.slice();

Related

Creating a JS Set inside an Array prototype

Disclaimer:
This is for learning purposes, and I already know adding methods to js built-in objects is not a good practice.
I'm trying to add a new method to the array prototype for a small hobby project:
Array.prototype.add = function (element) {
console.log('array before: ' + JSON.stringify(this));
let arr = this;
if (arr.length) {
let set = new Set(arr);
console.log('set before: ' + JSON.stringify(set));
console.log('adding element: ' + JSON.stringify(element));
set = set.add(element);
console.log('set after: ' + JSON.stringify(set));
} else {
arr.push(element);
}
console.log('array after: ' + JSON.stringify(arr));
};
On attempting to call the new method once it pushes as expected. On a consecutive call the "array before:" log prints as expected with the first push making up the contents of the array, but feeding the array to my Set constructor results in an empty set, as evidenced by my "set before:" and "set after:" logs both printing an empty {}. I'm unsure why the Set won't instantiate from the array, any help would be appreciated.
JSON.stringify always results in an empty object for sets
var s = new Set([1,2,3]);
console.log(s.size);
console.log(JSON.stringify(s));
Sets don't have properties that can be serialized. In other words, you are using the wrong method to verify whether the set is empty and are jumping to the wrong conclusion. Log set.size instead, or just set itself.
Related: JSON stringify a Set

Number Objects Passed Into Functions by Reference

I was reading somewhere that when we pass an object into a function "...JavaScript always uses the Object by reference when it passes as argument..." What I think this implies is (correct me if I'm wrong) is that if the function was to modify the object in some way, it would change the original defined object. I tried illustrating this with some code and it does do what I think it does but when I try the example in the blog post with a Number obj, it doesn't change the original value in that object. Please see my jsbin: https://jsbin.com/wociro/edit?js,console,output
console.clear();
/**myobject Object**/
function myobject() {
this.value = 5;
}
var o = new myobject();
console.log("Original value of o: " + o.value); // o.value = 5
function objectchanger(fnc) {
fnc.value = 6;
}
objectchanger(o);
console.log("New value of o: " + o.value); // o.value is now equal to 6
/*Number Object*/
var num2 = new Number(2);
console.log("Original value of num2: " + num2);
function numberChanger(fnc) {
return fnc + 1;
}
console.log("num2 after running numberChanger: " + numberChanger(num2));
console.log("New value of num2: " + num2); //looks the same
Am I missing something?
Number objects are still objects. So their value is a reference, and if a function alters a property of an object passed as an argument, that object will be affected outside the function.
function changer(obj) {
obj.foo = 'bar';
}
var num = new Number(123);
console.log(num.foo); // undefined
changer(num);
console.log(num.foo); // 'bar'
However, the value wrapped inside the number object is not stored as a property. It's stored as a [[NumberData]] internal slot. ECMAScript provides no way to alter that slot, so you can't change the number.
Your attempt of fnc+1 unwraps the number object to get its [[NumberData]], and adds 1 to that. But the result is just discarded, it's not stored back in the [[NumberData]] slot of fnc.
If you want to be able to achieve something analogous to changing the [[NumberData]], you can
function MyNumber(num) {
this.__number__ = +num;
}
MyNumber.prototype = Object.create(Number.prototype);
Object.getOwnPropertyNames(Number.prototype).forEach(function(prop) {
var desc = Object.getOwnPropertyDescriptor(Number.prototype, prop);
if(desc && desc.value && typeof desc.value == 'function') {
var native = desc.value;
desc.value = function() {
return native.apply(this.__number__, arguments);
};
Object.defineProperty(MyNumber.prototype, prop, desc);
}
});
var num = new MyNumber(123);
console.log(+num, num+'', num.toFixed(2)); // 123, "123", "123.00"
num.__number__ = 456;
console.log(+num, num+'', num.toFixed(2)); // 456, "456", "456.00"
I actually had a lot issues when I started getting into the object side of JavaScript myself. Best way I can explain is by these examples.
Objects link.
var obj = {a: 5};
var b = obj.a;
b = 2;
// obj.a: 2
// b: 2
This will link to the object value I believe. So if you change b it will also change obj.a.
HTML DOM object link with odd behavior
var x = document.getElementById("some_div_id");
x.innerHTML = "example"; // this works
var x = document.getElementById("some_div_id").innerHTML;
x = "example"; // this doesn't, it thinks that it's document.getElementById("some_div_id");
Took me time to figure what was wrong when I first did the second DOM method.
Variables are not linked but copied.
var a = 5;
var b = a;
b = 2;
// a: 5
// b: 2
As you can see, this doesn't link the value but creates a new one based from it.
Deep copying from objects trick.
function deepCopy(objValue) {
return JSON.parse(JSON.stringify(objValue));
}
var obj = {a: 5};
var b = deepCopy(obj.a);
b = 2;
// obj.a: 5
// b: 2
This was a trick given to me some time back when I had issues wanting a object value being stored in a variable and edited but without it being linked to the object value. After a while I found I never needed it after improving my coding skills.
Also last note. I read somewhere in clean JavaScript coding that you shouldn't need to use the new object method unless it's a Date() object or or simulated class, or you may run into typeof and value check issues with ===.
Can't be certain if this is error free but hope this helps explains better.
In Javascript, objects refer to an array, indicated by [] or an object {}. You can verify the type of the variable by using typeof. These are passed by reference.
typeof [2, 5, 3] //object
typeof { a: 10} // object
If you pass the object literal to a function and modify the value of the property 'a', it would result in the value being modified.

How to make an object "live" in Javascript without tying it to the DOM

I asked in IRC chat yesterday whether it was possible to have an element update with any changes to an object it references, rather than just keep the value it was given upon being declared. For example
Arr1 = [1,2,3]
i1 = Arr1.length-1
last1 = Arr1[i1]
Arr1.push(4)
Checking last1 at the end of this (arbitrary) example shows it has not updated to reflect the newly added value of 4, so objects in JS aren't "live" by default.
I'm a beginner but I'd worked this out from practice already, but was told this was actually the case... I guess my question wasn't understood.
NodeList objects are "live" however, and I'm wondering if there are other types of object in JS that do so, as it would obviously save lines of code spent updating them.
One important distinction here is that i1 does not "reference" anything here. It is simply storing the numeric result of the expression Arr1.length-1 when that line executed. Likewise, last1 may or may not reference the value that was the third element of Arr1 when line 3 executed, but it maintains no reference to Arr1 itself or anything about it.
As in some other programming languages, variables that are assigned to objects are references, so you can do this:
var obj1 = { prop1: "hello", prop2: "goodbye" };
var obj2 = obj1;
obj2.prop1 = "buongiorno";
console.log(obj1.prop1); // result is "buongiorno"
But this doesn't seem to be quite what you're describing.
It sounds like what you're describing is some sort of reactive programming. JavaScript doesn't really work the way you're imagining, but you could accomplish this using closures:
var Arr1 = [1,2,3];
var i1 = function() { return Arr1.length - 1; };
var last1 = function() { return Arr1[i1()]; };
console.log(i1()); // result is 2
console.log(last1()); // result is 3
Arr1.push(4);
console.log(i1()); // result is 3
console.log(last1()); // result is 4
Note that here, the () parentheses at the end are required to call these functions and get their current value.
One even trickier thing you could do is the following:
function capture(fcn) {
return { valueOf: fcn, toString: function() { return fcn().toString(); } };
}
var Arr1 = [1,2,3]
var i1 = capture(function() { return Arr1.length - 1; });
var last1 = capture(function() { return Arr1[i1]; });
console.log(last1 * 5); // result is 15
Arr1.push(4);
console.log(last1 * 5); // result is 20
Note however that this second technique has its limitations. You have to coerce the values into the type that you would expect or call their .valueOf() method in order for them to produce an actual value. If you just used:
console.log(last1);
You would not get any sort of friendly result.
I interpret your question as why are some variables which are copies are updated when you change the original value and others are not.
This is because some types of variables are Reference Types and others are Value Types. Numbers, dates and strings are value types and are copied whenever you assign them to a variable. Objects, Arrays (which are also an Object) are reference types and are not copied, just referenced.
This example is using value types and any change to the first variable will not be copied:
var foo = 1;
var bar = foo; // this is copying the value from the foo variable to bar
foo = 2;
console.log(bar); // 1
compared to the same thing but with a reference to an object:
var foo = {prop:1};
var bar = foo; // this is creating a reference to the foo object
foo.prop = 2;
console.log(bar.prop); // 2

jquery variable values bleeding

i am totally missing something here. I need to define a base variable. Then reference that base varialbe in a series of second variables. Each of the second variables will be provided alternate values. However I am seeing the modified values bleed back to the base variablle
for example
<script>
var a = [{a:1}];
var b = a;
b.a = 22;
alert('a-a equals' + a.a + ' and b-a equals ' + b.a);
</script>
I want a.a to equal 1 and b.a to equal 22. however I am seeing both equal 22. Can someone explain what I am missing?
You are missing the fact that var b = a; doesn't create a new copy of a into b, it actually assigns the reference of the object stored in a into b. What this means is that any actions you take against the object stored in b is happening to the object stored in a at the same time. That is because in reality they are the EXACT SAME object!
a and b are really just telling the code where it can find the underlying object in memory, and in this case they are both pointing to the same place.
var b = a; this will just copy the reference to the actual array (The array will not be copied)
You can copy your array like this -
var a1 = [{a:1}];
var a2 = JSON.parse(JSON.stringify(a1));
a2[0].a = 22;
console.log('a-a equals' + a1[0].a + ' and b-a equals ' + a2[0].a);
Or -
var a2 = $.map(a1, function (obj) {
return $.extend({}, obj);
});
Demo --> http://jsfiddle.net/vTpSk/
Javascript will not clone a reference type when you assign a to b. There are ways to do this if you want using jquery clone or extend.
for example, you could do
var a = $.extend({}, a, b);
Objects are passed by reference. When you do var b = a, since a is an array and arrays are objects, the array is passed by reference so b references the same array as a. Instead, you need to create a copy of a and store that in b.
To copy an array, you would just use a.slice(0)
// NOTE! this example doesn't work, explained below.
var a = [{a:1}];
var b = a.slice(0);
b[0].a = 22;
alert('a-a equals' + a[0].a + ' and b-a equals ' + b[0].a);
However, since your array contains an object, the object inside the array still references the same object! Therefore, you're going to have to instead loop over the array, re-creating the array from the inside out. jQuery's $.extend method can be used here:
var a = [{a:1}];
var b = [];
for (var i = 0; i < a.length; i++) {
b.push( $.extend({},a[i]) );
}
b[0].a = 22;
alert('a-a equals ' + a[0].a + ' and b-a equals ' + b[0].a);
http://jsfiddle.net/e9Lja/
an alternative is to use jquery's $.extend combined with $.makeArray:
var a = [{a:1}];
var b = $.makeArray($.extend({},a));
b[0].a = 22;
alert('a-a equals ' + a[0].a + ' and b-a equals ' + b[0].a);
http://jsfiddle.net/e9Lja/1/
though, i prefer the for loop or $.map(see pXL's answer) because $.extend isn't meant for copying arrays.

Javascript References Array

I have a big multidimensional array that holds references to objects and I need to be able to move a reference from one spot in the array to another and then delete the original reference (or set it to undefined). The problem with this is it then deletes the recent reference.
var data = [[new Obj, new Obj], [new Obj, new Obj]];
function move(fromx, fromy, tox, toy) {
data[tox][toy] = data[fromx][fromy];
delete data[fromx][fromy];
}
Edit: I mean both are gone. data[tox][toy] === undefined; Both references are destroyed, not just data[fromx][fromy]
Yeah, that's just the delete operator doing what it is supposed to do, which is delete the object which is referred to at [fromx,fromy]. Try simply setting data[fromx][fromy] to null or an undefined variable such as allYourBaseBelongToUs (At least I'd hope it's undefined) or if you're boring undefined is also undefined.
Sources:
https://developer.mozilla.org/en/JavaScript/Reference/Operators/Special_Operators/delete_Operator
http://www.openjs.com/articles/delete.php
var x = [[{'a':'1'}, {'b':'1'}], [{'c':'1'}, {'d':'1'}]]
x[1][0] = x[0][0]
x[1][1] = x[0][1]
delete x[0][0]
delete x[0][1]
console.log(x)
prints [[undefined, undefined], [Object { a="1"}, Object { b="1"}]]
What's your expected output ?
delete doesn't delete the object, it deletes the property that was referencing the object. In your move function, since you've just assigned that object to another property, you still have the object. (Whatever was previously in that slot in the array is toast, though.)
Your example above mostly works, here's an example (using a custom object so we can easily print out the contents of the arrays):
function T(id) {
this.id = id;
}
T.prototype.toString = function() {
return this.id;
};
var data = [[new T("0x0"), new T("0x1")],
[new T("1x0"), new T("1x1")]];
display("0: " + data[0].join(","));
display("1: " + data[1].join(","));
display("Moving 0x0 to 1x2");
move(0, 0, 1, 2);
display("0: " + data[0].join(","));
display("1: " + data[1].join(","));
function move(fromx, fromy, tox, toy) {
data[tox][toy] = data[fromx][fromy];
delete data[fromx][fromy];
}
function display(msg) {
var p = document.createElement('p');
p.innerHTML = msg;
document.body.appendChild(p);
}
Live copy
I wouldn't recommend delete in this scenario, though, since it's very unlikely it's what you actually want. If you want to keep the "cell" but remove its contents, just assign undefined to it. delete actually removes it.
Somewhat off-topic, but: JavaScript doesn't have multi-dimensional arrays. What you have there is an array of arrays. It's a distinction that does actually have a difference, in that the arrays held by the outermost array can be of different lengths. (Also, JavaScript arrays are sparse — they're not really arrays at all, barring an implementation choosing to make them so — so the arrays could have the same length but gaps.)

Categories