Update Nested Object Property in JavaScript - javascript

I have the following piece of code below:
class Set {
constructor() {
console.log("New Set!");
}
get repsCompleted() {
return this._repsCompleted;
}
set repsCompleted(value) {
this._repsCompleted = value;
}
}
class Exercise {
constructor(name) {
console.log(name);
this.name = name;
}
get sets() {
return [new Set(), new Set(), new Set()]
}
}
let barbellSquat = new Exercise("Barbell Squat");
let updatedBarbellSquat = updateRepsCompleted(barbellSquat);
console.log(updatedBarbellSquat);
function updateRepsCompleted(exercise) {
for (var i = 0; i < exercise.sets.length; i++) {
exercise.sets[i].repsCompleted = 5;
}
return exercise;
}
I have a class exercises, which has a property sets, which is an array of objects of type Set.
So I create a new object off the Exercise class then pass the object to a function, whose purpose is to update the set objects and set the property "repsCompleted" to 5.
Then I console.log the exercise object, only to find that repsCompleted is undefined. I can't figure out why.
Here's a fiddle with the code:
https://jsfiddle.net/the_archer/a09tpr63/10/

You shouldn't use a getter for sets. A getter or setter is used every time the corresponding property is read or assigned, to allow you to customize the way that property works. The getter isn't only used the first time the property is accesssed.
So every time you use exercise.sets it creates a new array containing 3 empty Set objects. sets should be an ordinary property so you can retrieve the same value every time. You can initialize it in the constructor.
If you want to hide the sets property, like you did with repsCompleted in the Set class, you can use the same method: use a different name for the actual property, and define a getter and setter that accesses it. You can also initialize it lazily by checking whether the property is set yet.
class Set {
constructor() {
console.log("New Set!");
}
get repsCompleted() {
return this._repsCompleted;
}
set repsCompleted(value) {
this._repsCompleted = value;
}
}
class Exercise{
constructor(name) {
this.name = name;
this._sets = null;
}
get sets() {
if (!this._sets) {
this._sets = [new Set(), new Set(), new Set()];
}
return this._sets;
}
set sets(value) {
this._sets = value;
}
}
let barbellSquat = new Exercise("Barbell Squat");
let updatedBarbellSquat = updateRepsCompleted(barbellSquat);
console.log(updatedBarbellSquat);
function updateRepsCompleted(exercise) {
for (var i = 0; i < exercise.sets.length; i++) {
exercise.sets[i].repsCompleted = 5;
}
return exercise;
}

Related

How to make a reference between objects created by class and extended class in javascript?

I have created two classes:
class MyFirstClass {
constructor(fname, lname, date) {
this.fname = fname;
this.lname = lname;
this.date = date;
}
}
class MySecondClass extends MyFirstClass {
constructor(id, optionalArg) {
super();
this.id = id;
}
}
And then created two objects like this:
let myFirstObj = new MyFirstClass("foo", "bar", "today");
let mySecondObj = new MySecondClass(1234); //***(1234, optionalArg)
Now, there are several ways to actualy pass properties from first class to another (or from first obj to second), but whatever I do second object doesnt REFER to the first one, it just creates its own "copy" of properties. So when I do this:
mySecondObj.fname = "someothername";
first object doesnt change - its not referenced to the second one (or the other way - doesnt work either).
My question is: How to solve this "conection" on classes (or out of them) to actualy reference new objects one to another?
I want to make it as simple as possible (thats why I left optional argument in second class).
Here is a try using get and set to maintain the reference connection between two object.
See below and read the comment.
class MyFirstClass {
constructor(fname, lname, date) {
this.f_name = fname;
this.f_name = lname;
this.d = date;
this.b = this;
}
set base(value){
this.b = value;
var a = this;
Object.keys(this).forEach(function(key){
if (a.b[key] != undefined)
a[key] =a.b[key]
});
}
set fname(value){
this.f_name = this.b.f_name = value;
}
get fname(){
return this.f_name;
}
get lname(){
return this.l_name;
}
set lname(value){
this.l_name =this.b.l_name= value;
}
set date(value){
this.d =this.b.d= value;
}
get date(){
return this.d;
}
}
class MySecondClass extends MyFirstClass {
constructor(id,base,optionalArg) {
super();
this.id = id;
// chooce to have a reference to another object as base
// now when you change either this or base class the both of them will change to
if (base){
this.base = base;
base.b = this;
}
}
}
let myFirstObj = new MyFirstClass("foo", "bar", "today");
// myFirstObj is optional.
let mySecondObj = new MySecondClass(1234, myFirstObj); //***(1234,base, optionalArg)
mySecondObj.fname = "test"
console.log(myFirstObj.fname)
Creating class inheritance does not mean they are implicitly sharing values by reference. So myfirstobj and mysecondobj would occupy their own space in memory, and changes made to either of them won't automatically change their counter part.

How to pass by reference or emulate it

I have two IFFE:
var Helper = (function () {
return {
number: null,
init: function (num) {
number = num;
}
}
})();
var Helper2 = (function () {
return {
options: {
number: [],
},
init: function(num){
this.options.number = num;
},
getData: function () {
return this.options.number;
}
}
})();
Helper2.init(Helper.number);
console.log(Helper2.getData());
Helper.init(5);
console.log(Helper2.getData());
What I want is
Helper2.init(Helper.number);
console.log(Helper2.getData()); // null
Helper.init(5);
console.log(Helper2.getData()); // 5
what I get is
Helper2.init(Helper.number);
console.log(Helper2.getData()); // null
Helper.init(5);
console.log(Helper2.getData()); // null
What techniques can be done to have it pass by reference, if it can?
JSBIN: https://jsbin.com/gomakubeka/1/edit?js,console
Edit: Before tons of people start incorporating different ways to have Helper2 depend on Helper, the actual implementation of Helper is unknown and could have 100's of ways they implement the number, so Helper2 needs the memory address.
Edit 2: I suppose the path I was hoping to get some start on was knowing that arrays/objects do get passed by reference, how can I wrap this primitive type in such a way that I can use by reference
Passing by reference in JavaScript can only happen to objects.
The only thing you can pass by value in JavaScript are primitive data types.
If on your first object you changed the "number:null" to be nested within an options object like it is in your second object then you can pass a reference of that object to the other object. The trick is if your needing pass by reference to use objects and not primitive data types. Instead nest the primitive data types inside objects and use the objects.
I altered you code a little bit but I think this works for what you were trying to achieve.
var Helper = function (num) {
return {
options: {
number: num
},
update: function (options) {
this.options = options;
}
}
};
var Helper2 = function (num) {
return {
options: {
number: num,
},
update: function(options){
this.options = options;
},
getData: function () {
return this.options.number;
}
}
};
var tempHelp = new Helper();
var tempHelp2 = new Helper2();
tempHelp2.update(tempHelp.options);
tempHelp.options.number = 5;
console.log(tempHelp2.getData());
First of all why doesn't it work:
helper is a self activating function that returns an object. When init is called upon it sets an number to the Helper object.
Then in Helper2 you pass an integer (Helper.number) to init setting the object to null. So you're not passing the reference to Helper.number. Only the value set to it.
You need to pass the whole object to it and read it out.
An example:
var Helper = (function () {
return {
number: null,
init: function (num) {
this.number = num; //add this
}
}
})();
var Helper2 = (function () {
return {
options: {
number: [],
},
init: function(obj){
this.options = obj; //save a reference to the helper obj.
},
getData: function () {
if (this.options.number)
{
return this.options.number;
}
}
}
})();
Helper2.init(Helper); //store the helper object
console.log(Helper2.getData());
Helper.init(5);
console.log(Helper2.getData());
I don't think you're going to be able to get exactly what you want. However, in one of your comments you said:
Unfortunately interfaces aren't something in javascript
That isn't exactly true. Yes, there's no strong typing and users of your code are free to disregard your suggestions entirely if you say that a function needs a specific type of object.
But, you can still create an interface of sorts that you want users to extend from in order to play nice with your own code. For example, you can tell users that they must extend from the Valuable class with provides a mechanism to access a value computed property which will be a Reference instance that can encapsulate a primitive (solving the problem of not being able to pass primitive by reference).
Since this uses computed properties, this also has the benefit of leveraging the .value notation. The thing is that the .value will be a Reference instead of the actual value.
// Intermediary class that can be passed around and hold primitives
class Reference {
constructor(val) {
this.val = val;
}
}
// Interface that dictates "value"
class Valuable {
constructor() {
this._value = new Reference();
}
get value() {
return this._value;
}
set value(v) {
this._value.val = v;
}
}
// "Concrete" class that implements the Valuable interface
class ValuableHelper extends Valuable {
constructor() {
super();
}
}
// Class that will deal with a ValuableHelper
class Helper {
constructor(n) {
this.options = {
number: n
}
}
getData() {
return this.options.number;
}
setData(n) {
this.options.number = n;
}
}
// Create our instances
const vh = new ValuableHelper(),
hh = new Helper(vh.value);
// Do our stuff
console.log(hh.getData().val);
vh.value = 5;
console.log(hh.getData().val);
hh.setData(vh.value);
vh.value = 5;

Automatically adding inheritance as properties on constructor, javascript

I'm moving a (heavy) file-parser to a webworker. Webworkers can only return serialized json. Hence, I cant return function that gives me object that inherit from a specific class:
getCollection: function getCollection(cls) {
if (!cls) {
return this.myObjects;
}
let collection = cls.toUpperCase();
let arr = [];
for (let i = 0; i < this.nrOfObjects; i++) {
if(classes[collection]){
if (this.myObjects[i] instanceof classes[collection]) {
arr.push(this.myObjects[i]);
}
}
}
return arr;
}
So for example getCollection('product') would return all instances that inherits from the class product
When I move the parsers to the webworker, I can't return this function. Is there anyway I can add the inheritance to my constructor in my classes instead?
export class Column extends BuildingElment {
constructor(GlobalId, OwnerHistory, Name, Description) {
super(GlobalId, OwnerHistory, Name);
this.description = Description
this.nameOfClass = 'Column';
this.inheritance = ['Root', 'Product', 'BuildingElement'] // Can this be somehow automatically generated??
}
}
Or if there is any other way I can work with webworkers and still get the inheritance.

Getters can set private variables

I think I've misunderstood something about OOP in JavaScript. As I understand it, the point of setting up a private variable with its own getter and setter functions is to protect it from accidental alteration elsewhere in the program. But I'm finding it very easy to accidentally change private variables using their getter functions, which means I must be doing something wrong.
function Phone(os) {
this.os = os;
var _phoneBook = []; // phoneBook is intended to be private
this.newNumber = function(phoneNumber) { // set new number
_phoneBook.push(phoneNumber);
}
this.listNumbers = function() { // get all numbers
return _phoneBook;
}
}
var andy = new Phone("Android");
andy.newNumber("555-123-4567");
console.log(andy.listNumbers()); // => ["555-123-4567"]
// You shouldn't be able to set a private property through a getter function, but you can.
var extendedPhoneBook = andy.listNumbers().push("123-456-7890");
console.log(andy.listNumbers()); // => ["555-123-4567", "123-456-7890"]
That Array is being passed as a reference, so when calling listNumbers() you are returning the Actual Array to the exterior. You can return copies of the Array with something like this:
function Phone(os) {
this.os = os;
var _phoneBook = []; // phoneBook is intended to be private
this.newNumber = function(phoneNumber) { // set new number
_phoneBook.push(phoneNumber);
}
this.listNumbers = function() { // get all numbers
return _phoneBook.slice(0);
}
}
you might consider changing function listNumbers to something like enumerateNumbers that takes a function that gets passed a string for each number in your list to hide the internal array, something like this:
function Phone(os) {
this.os = os;
var _phoneBook = []; // phoneBook is intended to be private
this.newNumber = function(phoneNumber) { // set new number
_phoneBook.push(phoneNumber);
}
this.enumerateNumbers = function(cb) { // get all numbers
for (var idx in _phoneBook) {
cb(_phoneBook[idx]);
}
}
}
var andy = new Phone("Android");
andy.newNumber("555-123-4567");
andy.enumerateNumbers(function(num) { console.log(num); })

Class inheritance and private variables in JS

Say I have this code:
function ParentClass()
{
var anArray = [ ];
this.addToArray = function(what)
{
anArray.push(what);
console.log(anArray);
};
}
FirstSubClass.prototype = new ParentClass();
FirstSubClass.prototype.constructor = FirstSubClass;
function FirstSubClass()
{
this.addToArray('FirstSubClass');
}
SecondSubClass.prototype = new ParentClass();
SecondSubClass.prototype.constructor = SecondSubClass;
function SecondSubClass()
{
this.addToArray('SecondSubClass');
}
When I run new FirstSubClass() I see a single value array in the console. And when I run new SecondSubClass(), again, I see a single value array.
However, why is it when I run them again (i.e. new FirstSubClass(); new SecondSubClass();) I then see the arrays added to rather than new ones being created?
The rationale here is that I'm creating new instances of a class, therefore why are they sharing the same private property?
How can I avoid this so when I do, for e.g., new FirstSubClass() I then see a single value array no matter how many times I create a new instance of the class?
Keep in mind that you've only called new ParentClass() once for each subclass. That means that the private array variable is part of the prototype object for those subclasses. There's only one prototype object, so there's only one array (per subclass).
Each call to new FirstSubClass() generates a new instance that shares the same prototype object. The call to addToArray() therefore adds an element to that same array that was created when the prototype object was created.
edit — if you want per-instance arrays, you'd have to do something like this:
function ParentClass() {
this.addToArray = function(value) { this.instanceArray.push(value); };
};
function FirstSubClass() {
this.instanceArray = [];
this.addToArray("First");
}
FirstSubClass.prototype = new ParentClass();
FirstSubClass.prototype.constructor = FirstSubClass;
First, sub-classing in JS is typically a bad idea, because people think that they're getting extension, where every instance has its own copy of properties and methods...
...really, they're getting public static access to the parent's stuff.
Even better, that public static stuff has no access to the encapsulated variables, so there's really no manipulation of private data, unless you're using private functions (with a public interface) to pass data to and collect return values from, the public static stuff.
var Parent = function () {
this.static_prop = 0;
this.static_method = function (num) { this.static_prop += 1; return num + this.static_prop; };
};
var Child = function (num) {
this.public_func = function () { num = this.static_method(num); };
};
Child.prototype = new Parent();
var child = new Child(13);
child.public_func();
Just calling this.static_method wouldn't help, because it would have 0 access to num, which means that you're wrapping things which you inherited to grant them access to use private data as inputs, which means that you're doing most of the writing you'd be doing anyway, regardless of inheritance, because your expectations of .prototype were backwards.
Might I suggest Dependency Injection, instead?
Component-based programs?
var Iterator = function () {
var count = 0,
min = 0,
max = 0,
reset = function () { count = min; },
next = function () { count = count >= max ? min : count; return count += 1; },
set_min = function (val) { min = val; },
set_max = function (val) { max = val; },
public_interface = { reset : reset, count : count, set_min : set_min, set_max : set_max };
return public_interface;
},
Thing = function (iter) {
var arr = [],
currentObj = null,
nextObj = function () {
currentObj = arr[iter.next()];
},
add = function (obj) {
arr.push(obj); iter.set_max(arr.length);
},
public_interface = { next : nextObj, add : add };
return public_interface;
};
var thing = Thing(Iterator());
thing.add({});
thing.next();
It's a convoluted example, but now every instance is going to be given exactly what it needs to do its job (because the constructor requires it -- or you can add the dependency later, through a public method, or as a public-property).
The interfaces for each module can now also get as simple and as clean as you'd like, as you don't have to wrap unexpected static-helpers to get private data...
Now you know what's private, you know what you're extending to the public, and you have clean ins and outs wherever you want to put them.
You are only constructing a new instance of ParentClass once per subclass and that is to apply it to your prototype. If you want each instance to have its own copy of the private array and its own copy of the function "addToArray" you will need to invoke the ParentClass constructor function within your other objects constructors:
function ParentClass(){
var anArray = [ ];
this.addToArray = function(what){
anArray.push(what);
console.log(anArray);
};
}
FirstSubClass.prototype = new ParentClass();
FirstSubClass.prototype.constructor = FirstSubClass;
function FirstSubClass(){
//call the parents constructor where "this" points to your FirstSubClass instance
ParentClass.call( this );
this.addToArray('FirstSubClass');
}
SecondSubClass.prototype = new ParentClass();
SecondSubClass.prototype.constructor = SecondSubClass;
function SecondSubClass(){
ParentClass.call( this );
this.addToArray('SecondSubClass');
}
try this:
http://jsfiddle.net/3z5AX/2/
function ParentClass()
{
var anArray = [ ];
this.addToArray = function(what)
{
anArray.push(what);
document.getElementById("i").value = anArray;
};
}
//FirstSubClass.prototype = new ParentClass();
FirstSubClass.prototype.constructor = FirstSubClass;
function FirstSubClass()
{
this.parent = new ParentClass()
this.parent.addToArray('FirstSubClass');
}
var q = new FirstSubClass();
var r = new FirstSubClass();
All Subclasses share the same parent class, thus the same private anArray
The solution is to use the Mixin pattern.
// I have the habbit of starting a mixin with $
var $AddToArray = function(obj) {
var array = [];
obj.addToArray = function(what) {
array.push(what);
console.log(array);
};
}
var FirstClass = function() {
$AddToArray(this);
}
var SecondClass = function() {
$AddToArray(this);
}

Categories

Resources