I have created a javascript class. When I create instances using the new keyword, I don't know why all of the instances share the same array data.
Can anybody explain why this happens? The Cards array in this example is referenced by all instances I created:
(function (scope) {
//Player class: player information, graphics object
//Contructor: init properties
function Player(opts) {
//INITIALIZE PROPERTIES
this.initialize(opts);
}
Player.prototype = {
AccountID: '',
Position: -1,
UserName: '',
Level: 0,
Avatar: 'av9',
Gold: 0,
Cards: [],
pos: { x: 0, y: 0 },
graphicsObj: {},
assets: {},
Status: 0,
initialize: function (opts) {
//copy all properties to new instance
this.copyProp(opts);
this.setCards();
this.createGraphicObject();
},
//copy properties to instance
copyProp: function (opts) {
for (var prop in opts) {
this[prop] = opts[prop];
}
},
...
...
setCards: function () {
//create new Cards data with default position
this.Cards[0] = new scope.Card({ image: this.assets['cards'] });
this.Cards[1] = new scope.Card({ image: this.assets['cards'] });
this.Cards[2] = new scope.Card({ image: this.assets['cards'] });
}
};
scope.Player = Player;
}(window));
In Javascript functions arrays are not copied. If you reference an array it will always refer to the same array.
If you don't want to pass a reference to the same array, you will have to copy the values over to a new array. This can be simple if the array only contains strings; it can also be complex if the array contains other arrays or objects.
Make a copy of your "cards" array before passing it o your new object:
this.assets['cards'].slice(0); //Makes a copy of your array
Related
I am trying to modify the property at a specific index in my Array of Objects, Somehow when I try to modify one value it modifies all the values occurring in the array of objects.
Can anyone know what I did wrong here? I don't want to return a new array and want to update the same array.
let options = [];
let va = {
stat: {
cde: null,
abc: null,
},
};
options.push(va);
options.push(va);
//accessing first element from index
options[0].stat['abc'] = 'test';
console.log(options);
Here you are pushing the same va object reference two times in an options array. Hence, it is updating the value in both the objects.
If you will try to log your options array you will get the [circular object Object].
If your objects will not contain the same reference, this will not be happening.
Demo :
let options = [];
let va = {
stat: {
cde: null,
abc: null,
},
};
let ba = {
stat: {
cde: null,
abc: null,
},
};
options.push(va);
options.push(ba);
//accessing first element from index
options[0].stat['abc'] = 'test';
console.log(options);
I have a function that accepts the following arguments:
set(section, field, pair, component, element, value)
The section, field, pair and component are just keys within the Object. They are way-points so we can travel down the hierarchy. Obviously section is the head, our entry point.
element is the target key and the value is the value that will be set.
Since, there are elements at different depths, I would like to do the following:
set('utility', null, null, null, 'exportId', 'banana')
This is for a shallow access, and internally it will do this:
dataObj[section][element] = value;
**/ As in
* data: {
utility: {
exportId: 'banana'
}
* }
*/
In other cases, when the element is deeper inside the Object, it may be required to do the following:
dataObj[section][field][pair][component][element] = value;
What would be the best way, to define the path to the element dynamically, so we skip the keys that are passed in as a 'null'?
for example:
set('history', 'current', null, null, 'fruit', 'apple')
**/ As in
* data: {
history: {
current: {
fruit: 'apple'
}
}
* }
*/
will internally be constructed as:
dataObj[section][field][element] = value;
as you might have noticed, we skipped [pair][component] because those slots were passed in as null(s).
Instead of having a long list of specific parameters, just pass an object to the function. this way you only pass what you need to and there won't be any "null" references to deal with on the call.
Using this implementation, this call can be shortened to something like this:
Your current implementation:
set('utility', 'current', null, null, 'exportId', 'banana')
Using an object as the parameter:
set({
section:'utility',
field:'current',
element: 'exportId',
value:'banana'
});
You could use rest parameters to get the arguments passed to an array. Then create an object using reduceRight
function set(...paths) {
return paths.reduceRight((r, key, i) => key !== null ? { [key] : r } : r)
}
console.log(set('history', 'current', null, null, 'fruit', 'apple'))
console.log(set('utility', null, null, null, 'exportId', 'banana'))
The above function will construct a nested object based on the paths. If you want to just update an existing object, you could traverse the object and set the value like this:
function set(dataObj, ...paths) {
let value = paths.pop(),
nested = dataObj;
for (let i = 0; i < paths.length; i++) {
const path = paths[i];
if (i === paths.length - 1 && nested)
nested[path] = value; // set the value if it's the final item
if (path !== null && nested)
nested = nested[path]; // get another level of nesting
}
return dataObj;
}
let obj = { utility: { exportId: 'banana' } }
console.log(set(obj, 'utility', null, null, null, 'exportId', 'orange'))
obj = {
history: {
current: {
fruit: 'apple'
}
}
}
console.log(set(obj, 'history', 'current', null, null, 'fruit', 'orange'))
Can I use getter/setters to do a similar task to an observer?
So for example if I assign an instance of a getter/setter to multiple objects, these will all be references, so if either object causes the getter to change the same code would run right?
I have tried the following code:
var obj = {
value: null,
get val() {
return this.value;
},
set val(x) {
console.log('set');
if (this.value !== x) {
console.log('value has been changed, do stuff!');
}
this.value = x;
}
}
var one = {
name: 'object 1',
value: obj /* Reference object */
}
var two = {
name: 'object 2',
value: obj /* Reference object */
}
var three = {
name: 'object 3',
value: obj /* Reference object */
}
Then run one.value.value = 2 which should fire the console log. However I just get 2 output in the console and no console.log.
Edit
Just saw where I was going wrong, I should be doing one.value.val = 2, this is starting to work, hang on.
Edit 2
Not too sure if this will function the way i'm expecting. I'm going to try and breakdown what i'm trying to do.
I have got an array of objects as follows:
var images = [
{ index: 0, object: HTMLElement, src: String, active: Object, alt: String },
{ index: 1, object: HTMLElement, src: String, active: Object, alt: String },
{ index: 2, object: HTMLElement, src: String, active: Object, alt: String },
{ index: 3, object: HTMLElement, src: String, active: Object, alt: String },
{ index: 4, object: HTMLElement, src: String, active: Object, alt: String },
{ index: 5, object: HTMLElement, src: String, active: Object, alt: String },
{ index: 6, object: HTMLElement, src: String, active: Object, alt: String }
];
This object will get duplicated a couple of times throughout my script however the active state needs to remain the same throughout all instances.
How i'm duplicating the object:
var newList = [];
for (var i = 0; i < _.images.length; i++) {
// Create new instances of the images
var img = document.createElement('img');
img.src = images[i].object.src;
img.classList.add('responsive-img');
img.alt = images[i].alt;
var span = document.createElement('span');
span.appendChild(img);
if (i === current) img.parentNode.classList.add('active');
var newImage = {
object: img,
index: i,
src: images[i].src,
active: images[i].active, // Use reference to old list
alt: images[i].alt
};
newList.push(newImage);
// Add each image to the gallery DOM
_.gallery.main.appendChild(span);
}
Basically now what I need to happen, is that if the active value is changed in either reference, then code should execute and add/remove a class from the object within that instance.
Does this make sense?
So if the following is run
images[0].active.val = 1
Then newList[0].object.classList.Add('active'); and images[0].object.classList.Add('active'); executes.
There is more code that should execute however lets take it a step at a time. I was using a Polyfill for Observer before however it's too heavy weight for what i want to do and over the top, also having issues with it on Internet Explorer.
I think the best way to do this is maybe some kind of callback from the setter so I could run unique code for each instance that this object is within?
It feels a bit hackish but adding a getter to active to set the object you are referring should work :
var active = {
_value : null,
object : null,
set value(v) {
_value = v;
this.object.style.display = v;
}
}
var images = [
{img : document.querySelector('.div1'), _active : active, get active () {this._active.object = this.img; return this._active}
},
{img : document.querySelector('.div2'), _active : active, get active () {this._active.object = this.img; return this._active}
}
]
images[0].active.value = 'none';
images[1].active.value = 'block';
<div class = 'div1'>div 1</div>
<div class = 'div2' style = 'display:none'>div 2</div>
I have an object, which has multiple children (this object is a serialized MongoDB record)
{
_id: '5881f6564d56a24f09562d9e',
key: 'value',
child: {
_id: '5882211a010ea9725a3efdd1',
key: 'value2',
param: 'param',
nested: {
_id: '588221592eb1530d6fcc252a',
arr: [ '588221b83f0f833ba132b670', '588224490a15d836d1ba56e4' ]
}
},
another: {
_id: '58822c4e48db7912655b3419',
param: 'value'
}
}
Before using this object in my application, I need to pass it through a function.
function processData(value) {
// do stuff
return value
}
However, this function (not controlled by me) doesn't support nested documents. To correctly process the object, it must start with the deepest nested document, replace it with the return value, then process the next level etc.
A 'document', is an object which has the key _id. There may be other objects without _id, these do not need to be processed. Therefore, it needs to be processed in the following order:
obj.child.nested = processData(obj.child.nested)
obj.child = processData(obj.child)
obj.another = processData(obj.another)
obj = processData(obj)
The order only matters for objects which have nested children (for example, obj.another could be processed before obj.child, as long as obj.child.nested was processed before obj.child).
This is what I have so far: http://jsbin.com/nenuvuwiwa/edit?js,console
This is what I ended up using:
function processData(obj) {
// Placeholder function to indicate it has
// been processed (in reality sets a load of
// prototype functions etc)
obj.processed = true;
return obj;
}
function processDoc(doc) {
for (key in doc) {
var val = doc[key];
if (val.hasOwnProperty('_id')) {
val = processDoc(val);
val = processData(val)
}
}
return doc;
}
var res = processDoc(obj)
I have a JavaScript object:
let component = {
id: comp.id,
name: comp.name,
attributes: {
initial: [
],
final: [
]
}
};
To prevent duplication in initial and final properties I want to use Set instead of array, but how do we declare Set without new operator inside object literal? I want to avoid doing the following if possible:
component.attributes.initial = new Set();
component.attributes.final = new Set();
There is nothing wrong with declaring an empty set inside the object literal:
let component = {
id: comp.id,
name: comp.name,
attributes: {
initial: new Set(),
final: new Set()
}
};
attributes: {
initial: new Set([]),
final: new Set([])
}
You can use any expression as value of a property in an object literal, which includes new Set:
{
initial: new Set(),
final: new Set(),
}