Javascript pass by object by reference then update - javascript

I've been searching all over SO and I know there are a lot of topics about this but I haven't found one that answered my question.
I saw a question about getting an object value back from a string like this:
function getPropertyByString(str) {
var properties = str.split(".");
var myTempObject = window[properties[0]];
for (var i = 1, length = properties.length; i < length; i++) {
myTempObject = myTempObject[properties[i]];
}
return myTempObject;
}
So if there is a global variable called myGlobalVar, you could pass the string 'myGlobalVar.someProp.stateName' and assumming that is all valid you would get back the value of stateName say Arizona for example.
How could I update that property to California now?
If I try
var x = getPropertyByString('myGlobalVar.someProp.stateName');
x = 'California';
that will update the value of x and not the object.
I tried
var x = getPropertyByString('myGlobalVar.someProp.stateName');
x.value = 'California';
that didn't work either.
Can someone please help me to understand this with my example?
Thanks

Try the following;
function setPropertyByString(path, value) {
var steps = path.split("."),
obj = window,
i = 0,
cur;
for (; i < steps.length - 1; i++) {
cur = obj[steps[i]];
if (cur !== undefined) {
obj = cur;
} else {
break;
};
};
obj[steps[i]] = value;
}
It'd work by using it such as;
setPropertyByString('myGlobalVar.someProp.stateName', 'California');
You can see it in action here; http://jsfiddle.net/xCK8J/
The reason yours didn't work is because strings are immutable in JavaScript. You are re-assigning the variable x with the value 'California', rather than updating the location it points to to be 'California'.
If you'd have done;
var x = getPropertyByString('myGlobalVar.someProp');
x.stateName = 'California';
You'd see it works; as you're manipulating the object pointed to by x, rather than reassigning x to be something else. The above is what the setPropertyByString() method does behind the scenes; it just hides it from you.

This would do the trick:
myGlobalVar.someProp.stateName = "California"
So would this:
myGlobalVar["someProp"].stateName = "California"
or this:
myGlobalVar["someProp"]["stateName"] = "California"
Alternatively,
var x = getPropertyByString('myGlobalVar.someProp');
x.stateName = "California"
Note that if, in my last example, I do something like this:
x = {stateName:"California"};
It will not change the value of myGlobalVar.someProp.stateName.
Using = assigns a new value to the variable on the LHS. This is not the same thing as assigning a new value to the referent of the variable.

Related

Using a variable increment to create new variables in Javascript

It might be a beginner's question, but I can't seem to find an answer on this.
The data it is getting is data out of a JSon file. I want it to loop through all the rows it is seeing. The loop works how it is written below and returns me the info I need with the rest of the code. I am trying to create multiple variables like testVar1, testVar2, testVar3, .... I don't know if it is possible to do it this way, or if I need to find another solution.
var i = 0;
for (var x in data) {
var testVar1 = data[0][1]; // works
var testVar[i] = data[0][1]; // doesn't
i += 1;
}
How can I make the testVar[i] work ?
What is the correct syntax for this?
Your code misses the initialization of your array variable: var testVar = [];.
⋅
⋅
⋅
Anyway, you may want to create those variables in the window object :
for (var i = 0; i <= 2; i++) {
name = 'var' + i;
window[name] = "value: " + i;
}
console.log(var0);
console.log(var1);
console.log(var2);
That way you can keep using the "short" variable name.
You can wrap all those variables in an object.
instead of:
var testVar1 = data[0][1];
Try:
var Wrapper = {};
//inside the for loop:
Wrapper["testVar" + i] = data[0][i];
...and so on.
You'd access them as Wrapper.testVar1 or Wrapper["testVar" + 1].
The problem you're having is pretty simple. You try to declare a variable as an array and in the same statement try to assign assign a value to a certain index. The reason this doesn't work is because the array needs to be defined explicitly first.
var testVar[i] = data[0][1];
Should be replaced with:
var testVar = []; // outside the loop
testVar[i] = data[0][1]; // inside the loop
Resulting in:
var i = 0,
testVar = [],
data = [
['foo', 'bar', 'baz'],
['kaas', 'is', 'baas']
];
for (var x in data) {
var testVar1 = data[0][1];
testVar[i] = data[0][1];
i += 1;
}
console.log('testVar1', testVar1);
console.log('testVar', testVar);
console.log('testVar[0]', testVar[0]);
console.log('testVar[1]', testVar[1]);
If i isn't an integer you should use an object instead. This can be seen in the answer of Tilepaper, although I advise against the use variables starting with a capital letter since they suggest a constant or a class.

Is there a way to loop this?

Is there a way to loop a declaration of a variable? just a loop to help me declare the variables so i dont have to do the monotonous work of change the numbers of the variable
var height1 = document.getElementById('height1').value;
var height2 = document.getElementById('height2').value;
var height3 = document.getElementById('height3').value;
var height4 = document.getElementById('height4').value;
var height5 = document.getElementById('height5').value;
var height6 = document.getElementById('height6').value;
var height7 = document.getElementById('height7').value;
var height8 = document.getElementById('height8').value;
var height9 = document.getElementById('height9').value;
var height10 = document.getElementById('height10').value;
var height11 = document.getElementById('height11').value;
var height12 = document.getElementById('height12').value;
var height13 = document.getElementById('height13').value;
var height14 = document.getElementById('height14').value;
var height15 = document.getElementById('height15').value;
var height16 = document.getElementById('height16').value;
This is not a right way of coding that, Just do like,
var heights = [];
Array.from(document.querySelectorAll("input[id^=height]")).forEach(function(itm){
heights.push(itm.value);
});
And now you can iterate the array heights to manipulate the values as per your requirement.
The logic behind the code is, querySelectorAll("input[id^=height]") will select the input elements that has id starts with the text height. Since the return value of querySelectorAll is a nodelist, we have to convert it as an array before using array functions over it. So we are using Array.from(nodelist). That will yield an array for us. After that we are iterating over the returned array by using forEach and pushing all element's value into the array heights.
This is almost always an indication that you want an array. Something like this:
var heights = [];
for (var i = 1; i <= 16; i++) {
heights.push(document.getElementById('height' + i).value);
}
Then you can reference a value from the array with something like:
heights[1]
Though technically since in JavaScript your window-level variables are indexable properties of the window object, you can essentially do the same thing with variable names themselves:
for (var i = 1; i <= 16; i++) {
window['height' + i] = document.getElementById('height' + i).value;
}
Then you can still use your original variables:
height1
Though in the interest of keeping things outside of window/global scope, maintaining the array seems a bit cleaner (and semantically more sensible).
This seems to be a good use case for an object:
var heights = {};
for (var i = 1; i <= 16; i++) {
heights[i] = document.getElementById('height' + i).value;
}
Maybe its time to introduce function:
Generally speaking, a function is a "subprogram" that can be called by code external (or internal in the case of recursion) to the function. Like the program itself, a function is composed of a sequence of statements called the function body. Values can be passed to a function, and the function will return a value.
function getHeight(id) {
return document.getElementById(id).value;
}
Call with the wanted id and use it like a variable.
getHeight('height1')
Normally you would put them in an array.
var heights = []
for (i = 1; i < 17; i++) {
heights[i] = document.getElementById('height' + i).value;;
}
Beware this will give you a hole at the start of the array ie heights[0] has nothing in it. If you use this to iterate it won't matter...
for (var i in heights) {
alert(heights[i]);
}

Why this json swap code does not work?

I have this fun :
swapjson = function (j1,j2)
{ var jj = JSON.parse(JSON.stringify(j1));
j1 = JSON.parse(JSON.stringify(j2));
j2 = jj;
}
I have also:
var myjson1 = {'x':1000, 'y':1000};
var myjson2 = {'x':2000, 'y':-2000};
swapjson (myjson1,myjson2);
console.log myjson1.x
1000 ?????
And I discover that inside the swapjson function the swap is made but not after call.
What is happen ? I dont understand what I'm doing bad...
Any help would be appreciated.
You can't replace the entire object like that, as the object itself isn't passed by referenced.
You can however change properties of the object passed, and it will stick, as a copy of the object is passed in.
So instead of parsing to strings and back to objects you can just iterate over the two objects keys, and replace one by one
swapjson = function (j1, j2) {
var temp = {};
for (key in j1) {
temp[key] = j1[key];
delete(j1[key]);
}
for (key in j2) {
j1[key] = j2[key];
delete(j2[key]);
}
for (key in temp) {
j2[key] = temp[key];
}
}
FIDDLE
You copy the value of myjson1 (which is a reference to an object and nothing to do with JSON) into j1, and the value of myjson2 into j2.
Then you overwrite the value of j1 and the value of j2.
You never change the values of myjson1 or myjson2.

Modify a global variable based on an object property

I'm trying to learn object oriented javascript and ran into the following problem:
I have an object constructor (is that the right term?) like this:
function itemCreator(itemName, itemType, itemPositionX, itemPositionY) {
this.itemName = itemName;
this.itemType = itemType;
this.itemPositionX = itemPositionX;
this.itemPositionY = itemPositionY;
allItems.push(this); //store all items in a global variable
}//end itemCreator;
//then I use it to create an object
megaRocket = new itemCreator (
'megarocket',
'item_megarocket',
108,
475
)
Now I realised I also need to map these objects to modify different global variables based on which "itemType" the object has. This is where I am stuck. How can I make a global variable that only objects with a specific itemType property can modify?
For example I would like to create an object that increments a variable called amountOfMegarockets, but only if the itemType for that object is "item_megarocket".
I later plan on looping an array of these items to see if player object touches them (to collect the item):
function checkForItems(){
var itemLen =allItems.length;
for (i=0; i < itemLen; i++){
var itemObject = allItems[i];
if ( //checking for "collisions" here
(ship.x < (itemObject.itemBitmap.x + itemObject.size) && (ship.x + shipWidth) > itemObject.itemBitmap.x) &&
(ship.y < (itemObject.itemBitmap.y + itemObject.size) && (ship.y + shipWidth) > itemObject.itemBitmap.y)
){
itemObject.actor.y = -500; //just removing the item from canvas here (temporary solution)
// Here comes pseudo code for the part that I'm stuck with
variableBasedOnItemObject.itemType++;
}
I hope my explanation makes sense to someone!
EDIT:
Bergi's answer makes most sense to me, but I can't get the syntax right. Here's how I'm trying to use Bergi's code:
var amounts = {},
allItems = [];
function itemCreator(itemName, itemType, itemPositionX, itemPositionY) {
this.itemName = itemName;
this.itemType = itemType;
this.itemPositionX = itemPositionX;
this.itemPositionY = itemPositionY;
(amounts[itemType]=2); // this is different from bergi's example because I need to set the initial value of the item to two
//I also shouldn't increase the item amount on creation of the item, but only when it's specifically called from another function
this.increaseCount = amounts[itemType]++; //this should IMO increase the itemType amount inside the amounts object when called, but it doesn't seem to work
}
//creating the object the way bergi suggested:
allItems.push(new itemCreator('shootUp001', 'item_up', 108, 475));
Now here's the problematic part:
function checkForItems(){
var itemLen =allItems.length;
for (i=0; i < itemLen; i++){
var itemObject = allItems[i];
if ( my condition here)
){
//code below is not increasing the value for the current itemType in the amounts object.
//Probably a simple syntax mistake?
itemObject.itemType.increaseCount;
}
}
}
Why is my call of itemObject.itemType.increaseCount; not increasing the value of amounts.itemType?
Increment a global variable called amountOfMegarockets, but only if the itemType for that object is "item_megarocket".
Don't use a global variable for each of those item types. Do use one object (in global or local scope) which counts the amouts of each type on its properties.
var amounts = {},
allItems = [];
function Item(itemName, itemType, itemPositionX, itemPositionY) {
this.itemName = itemName;
this.itemType = itemType;
this.itemPositionX = itemPositionX;
this.itemPositionY = itemPositionY;
amounts[itemType]++ || (amounts[itemType]=1); // count by type
allItems.push(this); // store all items
}
Notice that I wouldn't put all Item instances in an array by default, better omit that line and let it do the caller:
allItems.push(new Item('megarocket', 'item_megarocket', 108, 475));
you can do some things like
(function (global) {
// create a scope
var allItems = [];
global.itemCreator = function(itemName, itemType, itemPositionX, itemPositionY) {
this.itemName = itemName;
this.itemType = itemType;
this.itemPositionX = itemPositionX;
this.itemPositionY = itemPositionY;
allItems.push(this); //store all items in a global variable
}//end itemCreator;
})(this);
this will work. but seriously dont complicate too mutch your code juste to make private var. if someone want to cheat using debugger he will always found a way to do it.
---- edit
If you juste want access to global var base on itemType you can do some thing like:
function checkForItems(){
var itemLen =allItems.length;
for (i=0; i < itemLen; i++){
var itemObject = allItems[i];
if ( //checking for "collisions" here
(ship.x < (itemObject.itemBitmap.x + itemObject.size) && (ship.x + shipWidth) > itemObject.itemBitmap.x) &&
(ship.y < (itemObject.itemBitmap.y + itemObject.size) && (ship.y + shipWidth) > itemObject.itemBitmap.y)
){
itemObject.actor.y = -500; //just removing the item from canvas here (temporary solution)
// Here comes pseudo code for the part that I'm stuck with
if (window[itemObject.itemType + "Data"])) {
variableBasedOnItemObject.itemType++;
} else {
window[itemObject.itemType + "Data"] = {
itemType: 1
}
}
}
}
}

Function is rewriting variable. How is this possible?

This function seems to be rewriting the value for the 'pages' variable and I'm at my wit's end on this one.
I've tried returning the variable through a function, hardcoding the variable into the function, and a pile of other things, but this seems like it should work.
Any ideas?
EDIT:
The output from should be an array of objects formatted like this {default: "Tax Return", safe: "taxreturn"}. The function, when first called with getPages('Both', 'overview', null) and getPages('Both', null, 'overview') does this, but if you call it more times it will error and you will find that the 'pages' variable is now an array of objects.
var pages = [
"Dashboard",
"Overview",
"Contacts",
"Records",
"Cash Flow",
"Transactions",
"Income",
"Expenses",
"Tax Return"
];
var getPages = function(format, includeOne, excludeOne)
{
var pageStrings = pages;
if(includeOne)
for(var p = 0; p < pageStrings.length; p++)
if(uriSafe(pageStrings[p]) == uriSafe(includeOne))
pageStrings = [pageStrings[p]];
if(excludeOne)
for(var c = 0; c < pageStrings.length; c++)
if(uriSafe(pageStrings[c]) == uriSafe(excludeOne))
pageStrings.splice(c, 1);
var outputArray = [];
switch(format)
{
case 'UriSafe':
for(var i = 0; i < pageStrings.length; i++)
pageStrings[i] = uriSafe(pageStrings[i]);
break;
case 'Both':
for(var x = 0; x < pageStrings.length; x++)
{
pageStrings[x] = {
default: pageStrings[x],
safe: uriSafe(pageStrings[x])
};
}
break;
default:
}
function uriSafe(str)
{
return str.replace(' ', '').toLowerCase();
}
console.log(pageStrings);
return pageStrings;
}
var pageStrings = pages;
is creating a reference to the very same array object. When you access it via pageString, you alter the same object which pages does refer to. To create a copy of it (from which you then can splice, assign properties to, etc without altering pages), use
var pageStrings = pages.slice();
I think your confusion is around the following line
var pageStrings = pages;
This does not create a copy of pages it simply creates a reference to pages. This means any edit you make to the value of pageStrings (clearing, changing elements, etc ...) will show up on pages because they refer to the same variable.
If you want pageStrings to have a copy of the pages array then do the following
var pageStrings = pages.slice(0);
var pageStrings = pages; is your hangup. Keep in mind that when you use = in this way, your new var will be a reference if the argument on the right is and array, object, or function. With strings and numbers you will get the copy you were expecting.

Categories

Resources