Storing components in an Entity System - javascript

Note: this introduction is about entity systems. But, even if you don't know what these are, or haven't implemented them yourself, it's pretty basic and if you have general Javascript experience you will probably qualify more than enough to answer.
I am reading articles about Entity Systems on the T=machine blog.
The author, Adam, suggests that an entity should just be an id, that can be used to obtain it's components (ie, the actual data that the entity is supposed to represent).
I chose the model where all entities should be stored in "one place", and my primary suspects for implementing this storage are the array-of-arrays approach many people use, which would imply dynamic entity id's that represent the index of a component belonging to an entity, while components are grouped by type in that "one place" (from now on I'll just call it "storage"), which I plan to implement as a Scene. The Scene would be an object that handles entity composition, storage, and can do some basic operations on entities (.addComponent(entityID, component) and such).
I am not concerned about the Scene object, I'm pretty sure that it's a good design, but what I am not sure is the implementation of the storage.
I have two options:
A) Go with the array-of-array approach, in which the storage looks like this:
//storage[i][j] - i denotes component type, while j denotes the entity, this returns a component instance
//j this is the entity id
[
[ComponentPosition, ComponentPosition, ComponentPosition],
[ComponentVelocity, undefined, ComponentVelocity],
[ComponentCamera, undefined, undefined]
]
//It's obvious that the entity `1` doesn't have the velocity and camera components, for example.
B) Implement the storage object as a dictionary (technically an object in Javascript)
{
"componentType":
{
"entityId": ComponentInstance
}
}
The dictionary approach would imply that entity id's are static, which seems like a very good thing for implementing game loops and other functionality outside the Entity System itself. Also, this means that systems could easily store an array of entity ids that they are interested in. The entityId variable would also be a string, as opposed to an integer index, obviously.
The reason why I am against array-of-arrays approach is that deleting entities would make other entity ids change when a single entity is deleted.
Actual implementation details may wary, but I would like to know which approach would be better performance wise?
Things that I am also interested in (please be as cross-platform as possible, but if needed be, use V8 as an example):
How big is the overhead when accessing properties, and how is that implemented under the hoof? Lets say that they are being access from inside the local scope.
What is undefined in memory, and how much does it take? I ask this, because in the array-of-arrays approach all of the inner arrays must be of the same length, and if an entity doesn't have a certain component, that field is set to undefined.

Don't worry about the Array. It is an Object in JavaScript i.e. no "real" arrays, it's just the indices are a numeric "names" for the properties of the object (dictionary, hash, map).
The idea is simple, an Array has a length property that allows for loops to know where to stop iterating. By simply removing an element off the Array (remember, it's an Object) the length property doesn't actually change. So...
// create an array object
var array = ['one','two', 'three'];
console.log(array.length); // 3
// these don't actually change the length
delete array['two']; // 'remove' the property with key 'two'
console.log(array.length); // 3
array['two'] = undefined; // put undefined as the value to the property with key 'two'
console.log(array.length); // 3
array.splice(1,1); // remove the second element, and reorder
console.log(array.length); // 2
console.log(array); // ['one','three']
You've got to realize that JavaScript doesn't "work" like you expect. Performance wise objects and arrays are same i.e. arrays are accessed like dictionaries;
Scope is not like other "c style" languages. There are only global and function scopes i.e. no block scope (never write for(var i) inside another for(var i));
undefined in memory takes exactly the same amount as null . The difference is that null is deliberate missing of value, while undefined is just accidental (non-deliberate) missing;
Don't check if a field exists by doing if(array['two']) because, a field can actually hold the falsy values of undefined, null, 0, "", false and evaluate as false. Always check with if('two' in array);
When looping with for(key in array) always use if(array.hasOwnProperty(key)) so you don't iterate over a prototype's property (the parent's in a manner of speaking). Also, objects created by a constructor function might loop with the 'constructor' key also.

Related

array is not a data but a container but why does it have a data type?

My understanding of the data type is as follows. If we consider a machine like a mill that can take in rice, barley, corn, kidney beans, chickpeas, black beans, etc. So I understand the mill to be the program that processes data such as rice, barley, etc. The data are rice, barley, beans but they differ in types. Rice is of a different type than barley etc.
In javascript, i noticed objects mentioned under data types.
https://javascript.info/types#objects-and-symbols
It makes sense that 23 is data of type number, "cat" is data of type string, true is data of type "boolean" but object seems to be more like a data container rather than a data type. For instance, typeof([]) says it is an object. Now array seems like a data container rather than data and type of a data container doesn't make sense. To me, it makes sense that a data can have a type but a container where we put data having a type is confusing.
So I appreciate if someone can enlighten me. Array seems to be a container and not data and cannot have a type.
I appreciate any guide. Thanks!
On a general, theoretic level, an object consists of data that describes the object, and behaviours that act on that object. Before Object Oriented Programming, data was data, and code was code; if you needed to load some data, and draw it on the screen, chances are those two pieces of code would be in different places of your code base. OOP brings data and behaviour together for better conceptual organisation. It also allows some objects to share behaviour by sorting them into classes (e.g. "barking" is not something that needs to be coded for every dog individually) or by creating object prototypes (same idea, different approach). Furthermore, it allows different objects to specify different ways of performing the same behaviour (e.g. both cats and wolves know about hunting, though they go about it differently).
On a mechanical JavaScript-specific level, "object" is a data structure that can store various values under string keys. It is thus superficially similar to arrays, which again store values, but under integer keys.
However, "value" in JavaScript also includes functions, which is how JS object achieves the first objective (being a repository of related data and code that governs its behaviour). The second objective is achieved by prototypal inheritance:
let Mammal = function() {
}
let typicalMammal = new Mammal();
typicalMammal.legs = 4;
Mammal.prototype = typicalMammal;
let Dog = function() {
};
let typicalDog = new Mammal();
typicalDog.sound = function() { console.log("woof") };
Dog.prototype = typicalDog;
let fido = new Dog();
fido.colour = "yellow";
console.log("Fido:", fido);
fido.sound();
The third is also achieved: if we had a cat, we could just tell happy.sound() and it would do a "meow". We don't need to know if something is a dog or a cat, just that they know how to make a sound, and dynamic dispatch will do the rest.
It is also of note that when you invoke a function in an object with a dot notation like this, the function is interpreted as "method" - a function that knows which object it is acting on, by setting a special variable this. In this case, within the method sound, this would have the same value as fido.
Specifically with arrays, they are a kind of object that knows how to contain multiple values indicated by an integer index, how to be indexed, how to tell its length, how to iterate on its elements etc.
However, note that "variable" is something different entirely. One could conceptualise that variable is a data structure with a name and a value; but you can't typically access variable's name. A variable is not a value itself; you can't put a variable into a data structure, just copy its value there. It is more of a language artifact than a data type.
ECMAScript standard defines eight data types, 7 primitive types and Object.
In JavaScript, objects are a collection of properties and property values can be values of any type, for example an object with an attribute of type "number".
let obj = { num: 3 }
and you can test
typeof obj.num
will return "number", but
typeof obj
will return "object" I suppose because inheritance of prototype Object
All objects in JavaScript are descended from Object, all objects inherit methods and properties from Object.prototype.
In your example of an array. In javascript the array variable has a prototype of Array but that's an object that has a prototype of Object.
typeof([])
I think that's why is returning you "object".
Here are some links about data structures:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
JavaScript has only one data type for containers and it's called "object".
Arrays are containers and so their data type is "object". Like arrays there are other kinds of specialist object classes such as "function" (they contain code and are callable), sets and weak sets for example.
Significantly the JavaScript typeofoperator does not always return the data type of its operand. This is due to early implementation details of the operator which can not now be changed - and programs have long since adjusted to the actual implementation:
typeof null returns "object". The data type of null is "null".
typeof function returns "function". The data type of a function is "object".
...variable is a container and is not data
Variables are not so much a container as an identifier of where a value is stored. Variables in JavaScript are weakly typed which means the data type of a value is not determined by the variable (or object property) to which a value has been assigned but by the internal memory construct used to hold the value. That is to say, type is obtained from the value stored, not the variable used to access it.
Object values
An object value stored in a JavaScript variable is some kind of memory pointer to a data structure whose location and implementation details are private and only known to the JavaScript engine.
It makes sense to call objects values a data type in JavaScript for the same reason pointers in C have a data type depending on what they point to. It also explains why if you assign the same "object value" to two variables, they will both then point to the same object data set held at some unknown location in memory.
The major difference with JavaScript object values is that you can't perform pointer arithmetic on them and must rely on object property notations object.propertyName or object["propertyName"] to directly access properties of an object.
Array objects
JavaScript arrays are objects which use positive integer values, converted to a string, to access array entries being held as string properties of the array object.
Unlike a typed array, index values to access entries of an Array object do not have an upper bound nor do they need to be consecutive - memory is not allocated to store entries which have not been set.
TLDR;
JavaScript arrays are implemented as associative lookup tables. Their data type is "object" and they inherit properties and methods from Array.prototype and Object.prototype.
They are called "exotic" objects in ECMAScript standards because they monitor access to see if the property name used represents a non-negative integer and maintain the array object's length property if it is.
Early JavaScript documentation went out its way not to mention pointers, presumably to avoid confusing BASIC programmers. Documentation used "object" to mean a data structure, object values were stored as pointers, and the short hand property access operator chosen was . instead of the -> operator for pointer access used in the C language family.

Object unexpectedly being modified after push into array

I have what seems like it should be a simple operation. For each bridgedSection, I check for a potentialSection with an id that matches the bridged.referenceSection
Then I take that result, parse the HTML on the object with Cherio, make a slight modification (using an id for testing), and then store both the bridgedSection and the modified result on an object, then push that object to the array.
If I log the new object BEFORE pushing, I get the correct object values. If I log it from the array I get incorrect values only for reference.section. bridgedSection is fine, but reference.section matches across all entries in the array.
To say that I'm thoroughly flummoxed is an understatement. Can anyone shed some light on what I am (clearly) doing wrong?
var sectionCount = 0;
bridgedSections.forEach(bridged => {
var obj = potentialSections.find(obj => obj._id == bridged.referenceSection);
$ = cheerio.load(obj.html);
$(".meditor").html(bridged._id);// dropping the id here so it's easy to see if it was updated
obj.html = $.html();
obj.rand = Math.floor(Math.random() * 1000); // can't seem to add to obj either
var thisSection = {
referenceSection: obj,
bridgedSection: bridged,
}
console.log(thisSection) // correct value logged
currentSections.push(thisSection);
sectionCount++;
});
console.log(currentSections);
// this logs an array of the correct length but each
// {}.referenceSection is identical to the last entry pushed above
To try to clarify what both of the above folks are saying, the JavaScript language (like many others) has the concept of references, and makes very heavy use of that concept.
When one variable "refers to" another, there is only one copy of the value in question: everything else is a reference to that one value. Changes made to any of those references will therefore change the [one ...] underlying value (and, be reflected instantaneously in all of the references).
The advantage of references is, of course, that they are extremely "lightweight."
If you need to make a so-called "deep copy" of an array or structure or what-have-you, you can do so. If you want to push the value and be sure that it cannot be changed, you need to make sure that what you've pushed is either such a "deep copy," or that there are no references (as there obviously are, now ...) to whatever it contains. Your choice.
N.B. References – especially circular references – also have important implications for memory management (and "leaks"), because a thing will not be "reaped" by the memory manager until all references to it have ceased to exist. (Everything is "reference counted.")
And, all of what I've just said pretty much applies equally to every language that supports this – as most languages now do.
Javascript is passes function parameters by reference. This means the following happens:
derp = {a:1}
function passedByRef(param){
param['a'] = 2;
}
passedByRef(derp)
console.log(derp['a']) // 2
So when you pass a json object to a function, if you modify said object in the function it will change the original object. You probably want to make a deep copy of bridged before you assign it to thisSection because if you modify the version of bridged later on in thisSection it will modify the original object.
Here is a post that talks about cloning objects or you could look into something like immutable js
I think you need to look into Javascript deep copy.
You are modifying the original object when you modify the second assigned variable, because they are pointing to the same object. What you really need is to duplicate the object, not simply making a pointer to it.
Take a look at this:
https://scotch.io/bar-talk/copying-objects-in-javascript#toc-deep-copying-objects

Dynamic property as key VS List of objects in Javascript - which one will be more efficient

I need a data structure something like HashMap(in JAVA) where I want map status of user using user id as a key . So I can easily create a map dynamically like this:-
var mapOfIdVsStatus = {
123: "true",
456: 'false'
}
alert(JSON.stringify(mapOfIdVsStatus));
In my scenario, new user id and status will be added/updated very frequently that is
Object.keys(mapOfIdVsStatus).length;
will increase. But this will help to search status of a user in a faster way.
I can do this in an alternative way like this:
var user1={
id:123,
status: true
}
var user2={
id:456,
status: false
}
var listOfUser = [];
listOfUser.push(user1);
listOfUser.push(user2);
alert(JSON.stringify(listOfUser));
I can also search status in the list for a user but need extra effort like looping..etc
Considering a lots of Add, Search and Memory Optimization which one will be my best choice?
Behaviors would be like:
Are keys usually unknown until run time? -YES
Do you need to look them up dynamically? - YES
Do all values have the same type? -YES
Can they be used interchangeably?- NO
Do you need keys that aren't strings? - Always String
Are key-value pairs often added or removed?- Always added, No removal
Do you have an arbitrary (easily changing) amount of key-value pairs?- YES
Is the collection iterated? - I want to avoid iteration for faster
access.
The best choice is an ES6 Map. MDN gives some hints when to use it instead of a plain object:
If you're still not sure which one to use, ask yourself the following questions:
Are keys usually unknown until run time? Do you need to look them up dynamically?
Do all values have the same type? Can they be used interchangeably?
Do you need keys that aren't strings?
Are key-value pairs often added or removed?
Do you have an arbitrary (easily changing) amount of key-value pairs?
Is the collection iterated?
If you answered 'yes' to any of those questions, that is a sign that you might want to use a Map.
The object, hands-down. Property lookup will be, at worst, a kind of hashmap-style lookup rather than a linear search.
If you do this, it's best to create the object without a prototype:
var mapOfIdVsStatus = Object.create(null);
...so it doesn't inherit properties from Object.prototype.
On ES2015-compliant JavaScript engines, you might also look at Map. Map is specifically designed to be a simple key/value map, and so is unencumbered by any object-ness. On Chrome and Firefox, at least in my simple test, it performs as well or better than object lookup with string keys (this is Chrome; Firefox seems to be similar):
...but object lookup easily outpaces Map with number keys (this is also Chrome, on Firefox it was only twice as fast, not just under three times as fast):
But, those tests only test querying values once they've been added, not the tumult of adding new ones. (The updates you're talking about wouldn't matter, as you're changing the properties on the stored objects, not the thing actually stored, if I'm reading right.)
One really key thing is that if you need to remove entries, with an object you're better off setting the property's value to null or undefined rather than using delete. Using delete on an object can really, really slow down subsequent operations on it.

Maps vs Objects in ES6, When to use?

Ref: MDN Maps
Use maps over objects when keys are unknown until run time, and when
all keys are the same type and all values are the same type.
Use objects when there is logic that operates on individual elements.
Question:
What is an applicable example of using Maps over objects? in particular, "when would keys be unknown until runtime?"
var myMap = new Map();
var keyObj = {},
keyFunc = function () { return 'hey'},
keyString = "a string";
// setting the values
myMap.set(keyString, "value associated with 'a string'");
myMap.set(keyObj, "value associated with keyObj");
myMap.set(keyFunc, "value associated with keyFunc");
console.log(myMap.get(keyFunc));
What is an applicable example of using Maps over objects?
I think you've given one good example already: You at least need to use Maps when you are using objects (including Function objects) as keys.
in particular, "when would keys be unknown until runtime?"
Whenever they are not known at compile time. In short, you should always use a Map when you need a key-value collection. A good indicator that you need a collection is when you add and remove values dynamically from the collection, and especially when you don't know those values beforehand (e.g. they're read from a database, input by the user, etc).
In contrast, you should be using objects when you know which and how many properties the object has while writing the code - when their shape is static. As #Felix has put it: when you need a record. A good indicator for needing that is when the fields have different types, and when you never need to use bracket notation (or expect a limited set of property names in it).
I think that with ES2015's Map only two reasons are left to use plain objects:
You don't want to iterate over the properties of an object type at all
or you do but the property order doesn't matter and you can distinguish the program from the data level when iterating
When is the property order unimportant?
if you have only a single value and some functions that should be associated explicitly with it (like Promise - which is a proxy for a future value - and then/catch)
if you have a struct/record-like data structure with a static set of properties known at "compile time" (usually structs/records aren't iterable)
In all other cases you might consider using Map, because it preserves property order and separates the program (all properties assigned to the Map object) from the data level (all entries in the Map itself).
What are the drawbacks of Map?
you lose the concise object literal syntax
you need custom replacers for JSON.stringyfy
you lose destructuring, which is more useful with static data structures anyway
Use maps over objects when keys are unknown until run time, and when all keys are the same type and all values are the same type.
I have no idea why someone would write something so obviously wrong. I have to say, people are finding more and more wrong and/or questionable content on MDN these days.
Nothing in that sentence is correct. The main reason to use maps is when you want object-valued keys. The idea that the values should be the same type is absurd--although they might be, of course. The idea that one shouldn't use objects when keys are unknown until run time is equally absurd.
One of the difference between Map and Object is:
Map can use complex data type as its key. like this:
const fn = function() {}
const m = new Map([[document.body, 'stackoverflow'], [fn, 'redis']]);
m.get(document.body) // 'stackoverflow'
m.get(fn) //'redis'
watch out: For complex data type, If you want to get the value, you must pass the same reference as the key.
Object, it only accept simple data type(number, string) as its key.
const a = {};
a[document.body] = 'stackoverflow';
console.log(a) //{[object HTMLBodyElement]: "stackoverflow"}
Objects are similar to Maps in that both let you set keys to values, retrieve those values, delete keys, and detect whether something is stored at a key. Because of this (and because there were no built-in alternatives), Objects have been used as Maps historically; however, there are important differences that make using a Map preferable in certain cases:
The keys of an Object are Strings and Symbols, whereas they can be
any value for a Map, including functions, objects, and any primitive.
The keys in Map are ordered while keys added to object are not. Thus,
when iterating over it, a Map object returns keys in order of
insertion.
You can get the size of a Map easily with the size property, while
the number of properties in an Object must be determined manually.
A Map is an iterable and can thus be directly iterated, whereas
iterating over an Object requires obtaining its keys in some fashion
and iterating over them.
An Object has a prototype, so there are default keys in the map that
could collide with your keys if you're not careful. As of ES5 this
can be bypassed by using map = Object.create(null), but this is
seldom done.
A Map may perform better in scenarios involving frequent addition and
removal of key pairs.
MDN
This question is a duplicate of but until it's closed, here's my answer from over there:
In addition to the other answers, I've found that Maps are more unwieldy and verbose to operate with than objects.
obj[key] += x
// vs.
map.set(map.get(key) + x)
This is important, because shorter code is faster to read, more directly expressive, and better kept in the programmer's head.
Another aspect: because set() returns the map, not the value, it's impossible to chain assignments.
foo = obj[key] = x; // Does what you expect
foo = map.set(key, x) // foo !== x; foo === map
Debugging maps is also more painful. Below, you can't actually see what keys are in the map. You'd have to write code to do that.
Objects can be evaluated by any IDE:

When should I prefer a clone over an reference in javascript?

at the moment I'm writing a small app and came to the point, where I thought it would be clever to clone an object, instead of using a reference.
The reason I'm doing this is, because I'm collecting objects in a list. Later I will only work with this list, because it's part of a model. The reference isn't something I need and I want to avoid having references to outside objects in the list, because I don't want someone to build a construct, where the model can be changed from an inconsiderate place in their code. (The integrity of the information in the model is very important.)
Additional I thought I will get a better performance out of it, when I don't use references.
So my overall question still is: When should I prefer a clone over an reference in javascript?
Thanks!
If stability is important, then clone it. If testing shows that this is a bottleneck, consider changing it to a reference. I'd be very surprised if it is a bottleneck though, unless you have a very complicated object which is passed back and forth very frequently (and if you're doing that it's probably an indication of a bad design).
Also remember that you can only do so much to save other developers from their own stupidity. If they really want to break your API, they could just replace your functions with their own by copying the source or modifying it at runtime. If you document that the object must not be changed, a good developer (yes, there are some) will follow that rule.
For what it's worth, I've used both approaches in my own projects. For small structs which don't get passed around much, I've made copies for stability, and for larger data (e.g. 3D vertex data which may be passed around every frame), I don't copy.
Why not just make the objects stored in the list immutable? Instead of storing simple JSON-like objects you would store closures.
Say you have an object with two properties A and B. It looks like that:
myObj = {
"A" : "someValue",
"B" : "someOtherValue"
}
But then, as you said, anyone could alter the state of this object by simply overriding it's properties A or B. Instead of passing such objects in a list to the client, you could pass read-only data created from your actual objects.
First define a function that takes an ordinary object and returns a set of accessors to it:
var readOnlyObj = function(builder) {
return {
getA : function() { return builder.A; },
getB : function() { return builder.B; }
}
}
Then instead of your object myObj give the user readOnlyObj(myObj) so that they can access the properties by methods getA and getB.
This way you avoid the costs of cloning and provide a clear set of valid actions that a user can perform on your objects.

Categories