arrays disappearing in method scope - javascript

I am trying to create a class to my javascript game to add multiplayer but within the class i am having problems with the values of arrays changing as you can see in the sendNetEntities() function
class NET_IO{
//probably put address here
//I want to check for localhost to denote MASTER client
constructor(host, netlayer){
this.socket = io();
this.netLayer = netlayer
console.log(this.socket)
this.netEntities = this.netLayer.entities
//setInterval(() => {this.update()}, 200)
}
getNetEntities(){
this.socket.emit('getNetEntities', (net_entities) => {
console.log(net_entities)
this.netEntities = net_entities
})
}
sendNetEntities(layer){
var netEnt = this.netEntities
console.log(netEnt) //this returns [background: Entity, NIkTag: Entity, player: Entity]` the value i want
var ent = JSON.stringify(netEnt);
console.log(ent) //this returns []
this.socket.emit('sendNetEntities', ent)
}
update(layer, callback){
//check host if localhost dont retreive new data only send
this.sendNetEntities(layer)
callback(this.netEntities)
}
}
I think im having problems with variables somehow being references of something instead of instances. But im not entirely sure all of the rules behind that for javascript. can anyone help me shed some light on this problem. I'm willing to edit my question as needed
EDIT
further debugging leads me to believe that it must be some sort of problem with socket.io. if i run this this.socket.emit('sendNetEntities', {netEnt}) my return on the server is {netEnt:[]} I havent had problems like this in socket.io in the past. Am i doing something wrong. is socket.io the problem

Based on this:
//this returns [background: Entity, NIkTag: Entity, player: Entity]` the value i want
console.log(netEnt)
var ent = JSON.stringify(netEnt);
console.log(ent) //this returns []
I think you are treating an Array as an Object. In JavaScript, this is technically possible because almost everything is an Object, including arrays. However, this may lead to unexpected behavior:
// Create an array and an object
a = [] // an array
o = {} // an object
// Set some properties on both
a.p = 42
o.p = 42
// Show differences between arrays and objects:
console.log(a.constructor) // ƒ Array()
console.log(a) // [p: 42]
console.log(JSON.stringify(a)) // []
console.log(o.constructor) // ƒ Object()
console.log(o) // {p: 42}
console.log(JSON.stringify(o)) // {"p":42}
As you can see, JSON.stringify() ignores properties set on arrays.
So the solution is to use netEnt either as an array or as an object, without mixing the types:
// As an array, don't use property names. Use the integer array indices:
netEnt = [entity1, entity2, entity3]
background = netEnt[0]
nikTag = netEnt[1]
player = netEnt[2]
// As an object, property names can be used:
netEnt = {background: entity1, NIkTag: entity2, player: entity3}
background = netEnt.background
nikTag = netEnt.NIkTag
player = netEnt.player
update:
The fundamental problem is your classes use arrays, but access them as objects. The best solution is to change your classes so they either:
use arrays and access the arrays as arrays.
use objects and access the objects as objects.
Without seeing your class definitions, I cannot show you how to do this. However, it is as simple as changing the initial value of the class instances from [] to {}.
The following is a quick fix that serializes your array "objects" into true JS objects so JSON.stringify() will work as expected. However, in the future I highly recommend learning the difference between JS arrays and objects. This quick fix imposes a totally unnecessary performance penalty because JS arrays are being misused as objects:
sendNetEntities(layer){
var netEnt = this.netEntities
// Convert netEnt array "object" into true JS object
var trueObject = {}
for (prop in netEnt) {
trueObject[prop] = netEnt[prop]
}
var ent = JSON.stringify(trueObject);
this.socket.emit('sendNetEntities', ent)
}
Note in getNetEntities(), you will probably have to do the reverse: convert from true JS objects back to array "objects." I was unsure of the input format of net_entities, so I left this as an exercise.

Related

Weird Array Objects - JavaScript

Arrays are quite something in JavaScript when compared with other programming languages and it's not without its full set of quirks.
Including this one:
// Making a normal array.
var normalArray = [];
normalArray.length = 0;
normalArray.push(1);
normalArray[1] = 2;
normalArray; // returns [1, 2]
normalArray.length // returns 2
So yes, the above is how we all know to make arrays and fill them with elements, right? (ignore the normalArray.length = 0 part for now)
But why is it that when the same sequence is applied on an object that's not purely an array, it looks a bit different and its length property is off by a bit?
// Making an object that inherits from the array prototype (i.e.: custom array)
var customArray = new (function MyArray() {
this.__proto__ = Object.create(Array.prototype);
return this
});
customArray.length = 0;
customArray.push(1);
customArray[1] = 2;
customArray; // returns [1, 1: 2]
customArray.length // returns 1
Not entirely sure what's going on here but some explanation will be much appreciated.
This may not be the perfect answer, but according to my understanding of Javascript arrays, they are a little bit different than usual objects. (Mainly due to the fact that it maintains a length property, and Objects don't).
So if we take your code for an example:
var normalArray = [];
This is the right way to create an array in Javascript. But what about the below one?
var customArray = new (function MyArray() {
this.__proto__ = Object.create(Array.prototype);
return this
});
Are they same? Let's see..
Array.isArray(normalArray); // true -> [object Array]
Array.isArray(customArray); // false -> [object Object]
So it is clear that although you inherit from the array prototype, it doesn't really create an object with Array type. It just creates a plain JS object, but with the inherited array functions. That's the reason why it updates the length when you set the value with customArray.push(1);.
But since your customArray is only a regular object and for a regular JS object, [] notation is used to set a property, it doesn't update the length (because Objects don't have a length property)
Hope it's clear :)
The array you are trying to create is not a pure array (as you are perhaps aware). Its basically a JavaScript object and is supposed to behave like an object.
While treating an object like an array, its up to you to maintain all it's array like features.
You specifically have to assign a length property to it and you did it correctly.
Next, the push method from Array.prototype is supposed to insert an element to the array and increment the length property (if any), so it did increment 0 to 1. There you go, the length now is 1.
Next you used the literal notation of property assignment to Object, which is similar to something like customArray['someProperty'] = 1.
While using literal notation, no method from Array.Prototype is being invoked and hence the customArray object never knows that it has to behave like an Array and its length property remains unaffected. It simply behaves like an object and you get what you got.
Remember the length is just a property on Array class and this property is appropriately incremented and decremented by every method on Array.
Note: Array like objects are not recommended and its up to you entirely to maintain the index and other Array stuff for such objects.
From what I can see, you have a problem with your function:
return this
This should be
return (this);
Just fixes any potential errors you might have. Another thing is you're not using the var keyword to declare customArray. These errors might be breaking your code.

Adding fields to JS array

I've added a field "max" to the JS array prototype. I'm having trouble seeing this addition when I stringify the array. I've tried to overwrite the toJSON function but to no avail.
Array.prototype.max = 0;
Array.prototype.toJSON = function(){
var o = this;
o.max = this.max;
return o;
};
var a = [1,2,3];
a.max = 10;
console.log(JSON.stringify(a));
//'[1,2,3]'
I'd like to avoid doing something like {array_field:a,max_field:max} and just use the array object - is this possible? What am I doing wrong?
You have a few options here, none of them will do exactly what you want.
Since JSON arrays are strictly limited to integer keys[1], you can't send down the value of max directly.
Your choices include:
Using an object instead of an array. You loose all of the array magic, but it might work better for you.
Send down meta-data outside your array, such as a configuration object that includes both max and your array values
Force the array to be max long before sending it down. JSON will encode unused array elements with null.
Use a custom serialzer/deserializer. You could even code things in JSONS + the extras. In reality, this pretty much turns out the same as the second option.
[1] Unlike JavaScript Objects

How to merge these arrays/json objects?

I am a bit confused at this point on what is an object, what is an array, and what is a JSON. Can someone explain the differences in syntax between the two? and how to add items to each, how to merge each type, and such? I am trying to get this function to take the new information from a JSON object (I think) and merge it with some new information. This information will then be passed to a PHP script to be processed.
Here is the console output:
{"public":{"0":["el29t7","3bmGDy"]}}
{"public":"[object Object][object Object]"}
Here is the JS I am using:
/* Helper function to clean up any current data we have stored */
function insertSerializedData(ids, type) {
// Get anything in the current field
current_data = $('#changes').val();
if (!current_data) {
var data = {};
data[index++] = ids;
var final_data = {};
final_data[type] = data;
$('#changes').val(JSON.stringify(final_data));
} else {
current_data = JSON.parse(current_data);
var data = {};
data[index++] = ids;
// Does the index exist?
if (type in current_data) {
var temp_data = current_data[type];
current_data[type] = temp_data + data;
} else {
current_data[type] = data;
}
//var extra_data = {};
//extra_data[type] = data;
//$.merge(current_data, extra_data);
$('#changes').val(JSON.stringify(current_data));
}
console.log($('#changes').val());
}
The idea is if the key (public, or whatever other ones) doesn't exist yet, then to make it point to an array of arrays. If it does exist though, then that of array of arrays need to be merged with a new array. For instance:
If I have
{"public":{"0":["el29t7","3bmGDy"]}}
and I want to merge it with
["aj19vA", "jO71Ba"]
then final result would be:
{"public":{"0":["el29t7","3bmGDy"], "1":["aj19vA", "jO71Ba"]}}
How can i go about doing this? Thanks
Excellent two-part question. Overall, the second question is non-trivial because of the complexity of the first.
Question 1:
what is an object, what is an array, and what is a JSON. Can someone
explain the differences in syntax between the two?
Question 2:
and how to add items to each,
Question 3:
how to merge each type, and such?
Answer 1:
This is a common stumbling point because, JavaScript is more flexible than one might initially expect. Here is the curve.
In JavaScript everything is an object.
So here is the code for each:
//What is an object?
var obj = { };
var obj2 = { member:"value", myFunction:function(){} }
Above is an empty object. Then another object with a variable and a function.
They are called object-literals.
//What is an array
var array1 = [ ] ;
var array2 = [0,1,2,3,4];
Above is an empty array. Then another array with five Integers.
Here is the curve that causes confusion.
//Get elements from each of the prior examples.
var x = obj2["member"];
var y = array2[1];
What??? Both Object and Array are accessing values with a bracket?
This is because both are objects. This turns out to be a nice flexibility for writing advanced code. Arrays are objects.
//What is JSON?
JSON stands for JavaScript Object Notiation. As you might have guessed. Everything is an object... It is also an { }; But it is different because - it is used to transfer data to - and - from JavaScript, not actually used (commonly) in JavaScript. It is a file transfer format.
var JSONObject = {"member":"value"};
The only difference to the prior example is quotes. Essentially we are wrapping the object literal as a string so that it can be transferred to a server, or back, and it can be reinterpreted, very easily. Better than XML - because it does not have to be custom-parsed. Just call, stringify() or ParseJSON(). Google it. The point is... JSON can be converted into an object-literal JS object, and JS object-literals can be converted into JSON, for transfer to a server or a CouchDB database, for example.
Sorry for the tangent.
Answer 2:
How to add an item to each? Here is where the curve stops being a nuisance, and starts being awesome! Because everything is an object, it is all just about the same.
//Add to an object
var obj {member1:"stringvalue"}
obj.member2 = "addme"; //That is it!
//Add to an array
var array1 [1,2,3,4,5];
array1[0] = "addme";
array[6] = null;
//We shouldn't mix strings, integers, and nulls in arrays, but this isn't a best-practice tutorial.
Remember the JS object syntax and you may start to see a whole new flexible world of objects open up. But it may take a bit.
Answer 3: Ah, yeah... how to merge.
There are seriously (very many) ways to merge two arrays. It depends on exactly what you need. Sorted, Duplicated, Concatenated... there are a few.
Here is the answer!
UPDATE: How to make a beautiful multiple dimensional array.
//Multiple Dimension Array
var array1 = [1,2,3];
var array2 = [3,4];
var arraysinArray = [array1,array2]; //That is it!
Here is the curve again, this could be in an object:
var obj{
array1:[1,2,3],
array2:[3,4]
}
JavaScript is powerful stuff, stick with it; it gets good. : )
Hope that helps,
All the best!
Nash
In this case, think of a JavaScript's object literal {} as being like PHP's associative array.
Given that, an "array of arrays" actually looks like this (using your above desired output):
{public: [["el29t7","3bmGDy"], ["aj19vA", "jO71Ba"]]}
So here we have an object literal with a single property named "public" whose value is a 2-dimensional array.
If we assign the above to a variable we can then push another array onto "public" like this:
var current_data = {public: [["el29t7","3bmGDy"], ["aj19vA", "jO71Ba"]]};
// Using direct property access
current_data.public.push(["t9t9t9", "r4r4r4"]);
// Or using bracket notation
current_data["public"].push(["w2w2w2", "e0e0e0"]);
current_data's value is now:
{public: [
["el29t7","3bmGDy"],
["aj19vA", "jO71Ba"],
["t9t9t9", "r4r4r4"],
["w2w2w2", "e0e0e0"]
]}
So now "public" is an array whose length is 4.
current_data.public[0]; // ["el29t7","3bmGDy"]
current_data.public[1]; // ["aj19vA", "jO71Ba"]
current_data.public[2]; // ["t9t9t9", "r4r4r4"]
current_data.public[3]; // ["w2w2w2", "e0e0e0"]
MDN has very good documentation on Array for insight on other functions you might need.
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array
First is an object, that contains array, second is an array.
DEMO showing display output http://jsfiddle.net/GjQCV/
var object={"public":{"0":["el29t7","3bmGDy"]}};
var arr=["aj19vA", "jO71Ba"] ;
/* use object notation to add new property and value which is the array*/
object.public[1]=arr;
It'd be much more natural if {"0": ...} were a true array rather than an object, but anyway:
function maxKey(b) {
var max;
for( var key in b )
var max = key;
return max;
}
function merge(a,b) {
for( var key in a ) {
b[key] = b[key] ? (b[key][maxKey(b)+1]=a[key], b[key]) : a[key];
}
return b;
}
Note that this assumes you would insert at the next integer index
Arrays are a particular kind of Javascript object
JSON is a way of representing Javascript objects (and as such can represent arrays and more)
Objects are much more general, and can be simple objects that can be represented as JSON, or can contain functions and prototypes.
So, this is not an array of arrays (you would access items using JSON notation like myobj["0"]):
{"0":["el29t7","3bmGDy"], "1":["aj19vA", "jO71Ba"]}
This is an array of arrays, which means you can use the push method to add an item, and access items using array notation like myobj[0]:
[ ["el29t7","3bmGDy"], ["aj19vA", "jO71Ba"] ]
It seems like the structure you want is something like this:
var myobj = { "public": [ ["key", "value"], ["key", "value"] ] }
Then if you want to add/merge new items, you'd write this:
if (myobj["public"] != null) {
myobj["public"].push(["newkey", "newval"]);
} else {
myobj["public"] = ["newkey", "newval"];
}

Creating multi-dimensional arrays in javascript, error in custom function

I was trying to define an array (including other arrays as values) in a single javascript statement, that I can loop through to validate a form on submission.
The function I wrote to (try to) create inline arrays follows:
function arr(){
var inc;
var tempa = new Array(Math.round(arguments.length/2));
for(inc=0; inc<arguments.length; inc=inc+2) {
tempa[arguments[inc]]=arguments[inc+1];
}
return tempa;
}
This is called three times here to assign an array:
window.validArr = arr(
'f-county',arr('maxlen',10, 'minlen',1),
'f-postcode',arr('maxlen',8, 'minlen',6)
);
However in the javascript debugger the variable is empty, and the arr() function is not returning anything. Does anyone know why my expectations on what this code should do are incorrect?
(I have worked out how to create the array without this function, but I'm curious why this code doesn't work (I thought I understood javascript better than this).)
Well from what your code does, you're not really making arrays. In JavaScript, the thing that makes arrays special is the management of the numerically indexed properties. Otherwise they're just objects, so they can have other properties too, but if you're not using arrays as arrays you might as well just use objects:
function arr(){
var inc;
var tempa = {};
for(inc=0; inc<arguments.length; inc=inc+2) {
tempa[arguments[inc]]=arguments[inc+1];
}
return tempa;
}
What you're seeing from the debugger is the result of it attempting to show you your array as a real array should be shown: that is, its numerically indexed properties. If you call your "arr()" function as is and then look at (from your example) the "f-county" property of the result, you'll see something there.
Also, if you do find yourself wanting a real array, there's absolutely no point in initializing them to a particular size. Just create a new array with []:
var tempa = [];
Your code works. Just inspect your variable, and you will see that the array has the custom keys on it. If not expanded, your debugger shows you just the (numerical) indixed values in short syntax - none for you.
But, you may need to understand the difference between Arrays and Objects. An Object is just key-value-pairs (you could call it a "map"), and its prototype. An Array is a special type of object. It has special prototype methods, a length functionality and a different approach: to store index-value-pairs (even though indexes are still keys). So, you shouldn't use an Array as an associative array.
Therefore, their literal syntax differs:
var array = ["indexed with key 0", "indexed with key 1", ...];
var object = {"custom":"keyed as 'custom'", "another":"string", ...};
// but you still can add keys to array objects:
array.custom = "keyed as 'custom'";

In JavaScript, how do I access an object inherited from Array with the [] operator?

I have a situation, where I need to create a new JavaScript object that is inherited from Array. I am using the following code:
// Create constructor function.
var SpecialArray = function () {};
// Create intermediate function to create closure upon Array's prototype.
// This prevents littering of native Array's prototype.
var ISpecialArray = function () {};
ISpecialArray.prototype = Array.prototype;
SpecialArray.prototype = new ISpecialArray();
SpecialArray.prototype.constructor = SpecialArray;
// Use Array's push() method to add two elements to the prototype itself.
SpecialArray.prototype.push('pushed proto 0', 'pushed proto 1');
// Use [] operator to add item to 4th position
SpecialArray.prototype[4] = 'direct [] proto to 4';
// Create new instance of Special Array
var x = new SpecialArray();
// Directly add items to this new instance.
x.push('pushed directly on X');
x[9] = 'direct [] to 9'
console.log(x, 'length: ' + x.length);
Quite interestingly, the [] operation seem to be useless and the console output is:
["pushed proto 0", "pushed proto 1", "pushed directly on X"] length: 3
What am I missing here?
It is not possible to subclass the Array class and use t this way.
The best solution for you is to extend just the array class and use it as it is.
There are two other options that I do not like but they exist
http://ajaxian.com/archives/another-trick-to-allow-array-subclasses
http://dean.edwards.name/weblog/2006/11/hooray/
This is one of those that always trips people up. The length property only applies to the ordered elements. You can't extend an array then insert an arbitrary non-sequitous key and expect it to work. This is because the relationship between the length property and the array contents is broken once you extend the array. Pointy's link above does a very good job of explaining this in more detail.
To prove this add this to the end of your example:
console.log(x[4]);
As you can see your entry is present and correct, it's just not part of the ordered array.
Like everything else in javascript the Array object is just a Associative Array with string keys. Non numerical, non sequitous keys are hidden to fool you into thinking it's a 'proper' numerically indexed array.
This strange mixed design of the Array object does mean you can do some strange and wonderful things like storing ordered and unordered information in the same object. I'm not saying this is a good idea, I'm just saying it's possible.
As you will have noticed by now when iterating structures like this the non sequitous keys don't appear which makes sense for the general use case of arrays for ordered information. It's less useful, or in fact useless when you want to get keyed info. I would venture that if ordering is unimportant you should use an object not an array. If you need both ordered and unordered store an array as a property in an object.
The best way I have found to create a child prototype of an "Array" is to not make a child prototype of "Array" but rather create a child of an "Array-Like" prototype. There are many prototypes floating around that attempt to mimic the properties of an "Array" while still being able to "inherit" from it, the best one I've found is Collection because it preserves the ability to use brackets []. The major downfall is that it doesn't work well with non-numeric keys (i.e. myArray["foo"] = "bar") but if you're only using numeric keys it works great.
You can extend this prototype like this:
http://codepen.io/dustinpoissant/pen/AXbjxm?editors=0011
var MySubArray = function(){
Collection.apply(this, arguments);
this.myCustomMethod = function(){
console.log("The second item is "+this[1]);
};
};
MySubArray.prototype = Object.create(Collection.prototype);
var msa = new MySubArray("Hello", "World");
msa[2] = "Third Item";
console.log(msa);
msa.myCustomMethod();

Categories