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
}
}
}
}
}
Related
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]);
}
1.I want to push id[i] into global array.After I pushed then it will be id.length numbers items in each array,but it will be error and says y is not a function.how to solve it?
2.the sendrequest in the bottom will send a xmlhttprequest to server,but I don't understand why it will run first and then the function(parResponse) will be fire after tmpReadRequest.sendReadRequest(); has down.
Thanks a lot
var tes0=new Array();
var tes1=new Array();
var tes2=new Array();
var tes4=new Array();
var tes5=new Array();
function ReadValuePath(id, Variable,id_2) {
var tmpReadCB = function(parResponse)
{
for (var tmpIndex = 0; tmpIndex < parResponse.length; tmpIndex++)
{
var tmpItemValue = parResponse[tmpIndex];//console.log(tmpItemValue);
var tmpValue = (tmpItemValue.mItemValue) ? tmpItemValue.mItemValue : tmpItemValue.mItemResultId;
if(document.getElementById(id[tmpIndex]) != null && document.getElementById(id_2[tmpIndex]).value != 0)
{
document.getElementById(id[tmpIndex]).value = parseFloat(tmpValue).toFixed(2);
}
}
return true;
}
var tmpReadRequest = new OPCReadRequest("DE", tmpReadCB);
for(var z=0;z<5;z++ ){
for(var i = 0; i < id.length; i++)
var y="tes"+z;
y.push(id[i]);
tmpReadRequest.addItem("ab", Variable[i]);
}
}
tmpReadRequest.sendReadRequest();
}
"A variable declared outside a function, becomes GLOBAL.
A global variable has global scope: All scripts and functions on a web page can access it. " # Source
Y is not an array so .push() won't work on it. # Source
To access the global scope through a string literal like you are trying you can use the window object which is the current global scope.
So in your case it would be window[y].push(id[i]);
Another option would be to change you scructure slightly as personally i don't like accessing the window directly.
so you could define your arrays like
var arrays = {
tes0: [],
tes2: [],
tes3: [],
tes4: [],
tes5: [],
}
and access them like:
arrays[y].push(id[i])
EDIT according to comments
So you want to access global variable in a loop. You are half way there. What your doing is building a string y which contains the property name then using that in the square brackets to access that property.
So with the window option that would be:
for(var z=0;z<5;z++ ){
var y="tes"+z;
for(var i = 0; i < id.length; i++)
window[y].push(id[i]);
}
}
or with the second object option
/*
Because arrays is an object we can use Object.keys
This will return an array of the keys in our object which we can loop over to access each one
*/
Object.keys(arrays).forEach(function(key) {
for(var i = 0; i < id.length; i++)
arrays[key].push(id[i]);
}
});
Hope that helps explain
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.
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.
This partial code I hacked in to JamieMThomas's JQuery plugin that combines the Microsoft JQuery template and linking plugins together (declarative linking in the templates). I wanted to reference a variable tree like "A[0].B[0].C[0].myProperty". You can skip down to the bottom as I just put this in for reference:
var extVar = $.extend({ name: elem.name },
{ convert: binding.converter.convertBack,
convertBack: binding.converter.convert });
// binding.field is a string pointing to a variable to map
var a = binding.field.match(/([A-Z]+)\[\d+\]/g); // Find all array references
// If we have arrays, we need to create the corresponding hierarchy in "mapping"
if ( a != null)
{ b = mapping; // mapping (object) will reference a variable to map
for( i = 0; i < a.length; i++) // for each array found
{ var arr = a[i].match(/[A-Z]+/); // array's name
b[arr] = []; // make mapping match our binding.field text
var idx = a[i].match(/\d+/g); // index value
if( a[i+1] !== undefined ) // is the next item an array?
b[arr][idx] = []; // Yes, match the array
else
b[arr][idx] = {}; // No, match an object
b = b[arr][idx] ; // Reference LPC[x] // reference the next child
}
}
eval('(mapping.' + binding.field + ' = eval("extVar") )');
This eval at the bottom ends up running the below code. How would you rewrite this to not include the eval statement?
mapping.A[2].B[1].C[5].myProperty = A[2].B[1].C[5].myProperty;
In javascript, just as you can do object[propertyName] to read stuff you can do object[propertyName] = value to assign stuff.
The rest is here: How to turn this JavaScript string "myArray[0].myPrice" in to a reference to myPrice?
Basically:
var data = mapping,
chain = binding.field.split(/[\.\[\]]+/);
// If the last character of binding.field is `]` we'll get "" in the end of chain
if (!chain[chain.length - 1]) {
chain.splice(-1);
}
var n = chain.length;
for (var i = 0; i < n - 1; i++) {
data = data[chain[i]];
}
data[chain[n - 1]] = extVar;
// Embrace JavaScript Awesomeness!