I have a Angular service and in it I have variables like this:
export class MyService {
someVariableA = 1;
someParams = {
someVariableB,
otherVariable: this.someVariableA
};
}
and in a component I set the 'someVariableA' to 3
this.myService.someVariableA = 3;
and I want 'otherVariable' to get that value 3 as well, but it doesn't. It remains 1 when I go to get the value.
let v = this.myService.someParams.otherVariable;
Is it possible to set 'otherVariable' this way or any other way via 'someVariableA'?
As #Zulwarnain answered, 1 is a number or a primitive data type. Primitive data types in javascript are passed by value, not by reference which you seem to be expecting here.
An easy fix for this is to assign a function to otherVariable instead. Now just invoke the function someParams.otherVariable() and it will return the value of someVariableA. No need to make this complicated.
export class SingletonService {
public someVariableA = 1;
public someParams = {
otherVariable: () => this.someVariableA
};
}
This is basic javascript with multiple sources covering the subject.
https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0
I concur with this answer that you will have a better time if you use a reference type like an object/array instead of a primitive value type like a number. By adding one layer of indirection (e.g., someVar = 123 becomes someVar = {value: 123}) you could very easily get similar functionality to what you're seeking.
If, however, your use case requires an object's property to directly act like a reference to a primitive value type stored somewhere else, you can get this behavior by implementing the property as a getter and setter pair. It's more complicated, but it acts the way you want.
Here's an example:
class MyService {
someVariableA = 1;
someParams: {
someVariableB: number;
otherVariable: number;
};
constructor() {
this.someVariableA = 1;
const that = this;
this.someParams = {
someVariableB: 2,
get otherVariable() {
return that.someVariableA
},
set otherVariable(val: number) {
that.someVariableA = val;
}
}
}
}
Note that in order for the otherVariable getter and setter to be able to access the right context, I had to move the code into the constructor and copy this into a new variable I called that. The this context of a getter/setter refers to the object it's a member of, and not some this from an outer scope.
Let's make sure it works:
const ms = new MyService();
ms.someVariableA = 100;
console.log(ms.someParams.otherVariable); // 100
ms.someParams.otherVariable = -5;
console.log(ms.someVariableA); // -5
Looks good; changes to ms.someVariableA are immediately reflected in ms.someParams.otherVariable, and vice versa. All right, hope that helps; good luck!
Playground link to code
You are assigning the value type this will not work like you want. you need to assign reference type
obj ={someVariableA : 1};
someParams = {
otherVariable: this.obj
};
in the above code, if you change the value of obj.someVariableA it will also change the value of someParams.otherVariable
I am expexting that you have knowledge about reference type and value types variables
click here for demo
I don't think you want to do that. I believe you are getting a new instance of the service each time you call it, so the variables get reset.
you might want to set that variable in localStorage instead, and then have the service retrieve it from localStorage. That way it will always be getting whatever it was last set to.
or just pass that variable into your service call, instead of trying to use a local service variable.
Related
Assume we have some classes A and B:
Class A {
constructor(a) {
this.a = a;
};
showInfo() {
console.log(this.a)
};
};
Class B {
constructor(b) {
this.b = b;
};
printText() {
console.log('its B!');
};
};
Then we create an instance of B like this:
const objB = new B(
new A(3)
);
So now we have objB with its own method inside - printText, and we surely can call it.
But what if i want somehow when calling not existing method in objB to make it pass through to encapsulated A class in there and look for invoking this method on him, like this: objB.showInfo() - to give me 3 here ?
Same story, but at this time i want when calling not existing method on A to make it pass through to B outside (like that printText)?
P.S. Don't wanna use super() and inheritance, just composition and wrapping objects, hope you've got the point.
Just a little warning at the start: this might make your program harder to debug, and it also might be a little complicated for something like this. As others have suggested, you should probably investigate other options which may be simpler and also less in the way of everything else your code does.
Here's the code which provides the functionality:
function makeGetProxy(t){
return new Proxy(t, {
get(obj,prop){
if(prop in t){
return t[prop];
}else{
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
var val = t[keys[i]];
if(prop in val){
return val[prop];
// what about a recursive function?
}
}
return undefined;
}
}
});
}
And one itty bitty change to your constructor in B:
class B {
constructor(b) {
this.b = b;
return makeGetProxy(this);
};
printText() {
console.log('its B!');
};
};
If you want, you can also do the same to A.
Let's slow down. What just happened? I'll explain.
Since the properties we might request don't already exist, we're going to have to use a getter (see resources) to properly send back the value required. But, since we don't know the property names, we need a Proxy (see resources) to have a "catch-all" kind of get method.
The proxy will check if the property requested prop already exists, and if so, returns it. If it doesn't exist, it checks all of your properties' properties (all of the sub-properties).
The first result it gets, it returns it. This might cause unexpected bugs in your program. If it doesn't find it, it simply returns undefined.
Then, the proxy is generalized into a function for reuse in multiple classes (as you requested).
So, this can get the properties of a class and the properties of a class' properties, but if you need to go further (with your C class that doesn't exist yet), you can use a recursive function. I currently don't have the implementation for that recursive function, but in my head it would comprise mostly of a modified version of the else block in the makeGetProxy function.
Again, be careful with this code. It might get in the way of other things and cause unnecessary difficulty in debugging.
Resources:
Getters (MDN)
Proxy (MDN)
I borrowed some code from this answer and got the Proxy idea from this answer.
Is it possible to convert a var to a const?
Say in a scenario like this:
var appname = ''
function setAppName(name) {
appname = name // can I convert this to const now?
}
Basically, this is for a parameter level. Parameter needs to be accessible throughout the file and I want to make it const after first assignment. Is this possible?
To add: If there is any other way besides creating an object with to contain this parameter, it would be better (for a more straightforward solution). I will be working on multiple parameters like this and freezing an object will cause the entire object to freeze. I am hoping to do this on the parameter level.
So either change the parameter to become const. OR (this one I dont think is possible) change the scope of the paramter to global:
function setAppName(name) {
const appname = name // can I change the scope and make this global?
}
Thanks.
Put your app name in an object and freeze it.
let appSettings = { name: "" };
function setAppName(name) {
appSettings.name = name;
Object.freeze(appSettings);
}
Freezing prevents adding, removing and modifying the values of the properties in your object so you should call Object.freeze only once after all other settings (if there are more variables you want to make constant) have been set.
You can do this using a object.
const globals = {
appname: ''
}
function setAppName(name) {
globals.appname = name;
Object.freeze(globals)
// After this point, value of appname cannot be modified
}
Thank you for all your inputs.
I was able to find a workaround for what I was trying to achieve.
var appname = ''
function setAppName(name) {
if (appname === '') {
appname = name // can I convert this to const now?
}
}
Although this doesnt convert it to const, I just added a guard on the setter and it will not be able to overwrite the value now (unless otherwise I am going to initialize again to an empty string).
It is not fool-proof, but this will address my need.
Thanks all!
Is it possible to keep an object reference without using an holder object in javascript?
Currently when an object gets overridden I sometimes lose the reference to the "current" object state illustrated in the snippet below;
Is there a way to put a "pointer" in an array or not?
EDIT
To the questions asked:
What I have in the objects I have are references to form fields. Some of these are text fields, some of them are textareas, some of them checkboxes.
I wish to keep a map next to the direct referene of what type they are.
basicaly it would be
obj {
this.text1 = createTextField();
this.text1.datepicker();
this.text2 = createTextField();
this.area1 = createArea();
this.check = createCheck();
this.datefields = [this.text1];
this.checkboxes = [this.check];
}
So I can use the datefields/checkboxes array as a checkpoint to validate against which type a field is/should behave.
Currently I use
function datefields() { return [this.text1]; };
But I'd like to know if there's a better way to do this than to intantiate a new array when I need to check it.
I know there is a way with observers to mimic pointer behaviour, and i've fiddled with those and have some good results with that, i'm just curious if there are other ways i'm not aware of.
function myObject() {
this.myvalue = null;
this.arr = [this.myvalue];
}
myObject.prototype.alter = function() {
this.myvalue = "hello";
}
var x = new myObject();
var elem = document.getElementById('results');
function log(message) {
elem.appendChild(document.createTextNode(message));
elem.appendChild(document.createElement('br'));
}
log("x.myvalue = "+x.myvalue);
log("x.arr[0] = "+x.arr[0]);
log("calling alter");
x.alter();
log("x.myvalue = "+x.myvalue);
log("x.arr[0] = "+x.arr[0]);
<div id="results"></div>
Simple answer: Only objects (including all subtypes) are passed by reference in JS. All other simple values are copied.
For a bit more detail I would recommend reading You Don't Know JS: Types & Grammer but specifically the section Value vs Reference in Chapter 2:
In JavaScript, there are no pointers, and references work a bit differently. You cannot have a reference from one JS variable to another variable. That's just not possible.
Quoting further on:
Simple values (aka scalar primitives) are always assigned/passed by value-copy: null, undefined, string, number, boolean, and ES6's symbol.
Compound values -- objects (including arrays, and all boxed object wrappers -- see Chapter 3) and functions -- always create a copy of the reference on assignment or passing.
There are plenty of examples included to show these points. I would highly recommend reading through to get a better understanding of how values/references work in JS.
There is no pointers in Javascript, though you could cheat a little using a wrapper object. Here is a minimal implementation of such an object:
var Wrapper = function (value) {
this.value = value;
};
Wrapper.prototype.valueOf = function () {
return this.value;
};
Then you may use it in place of the original value:
function myObject() {
this.myvalue = new Wrapper(null); // wrapper
this.arr = [this.myvalue];
}
myObject.prototype.alter = function() {
this.myvalue.value = "hello"; // notice the ".value"
}
The rest of your code needs no tweaks.
Been trying to instantiate an object (called "isSize" below) and assign it to another existing variable (called "sizeObject"), here is the first object ("isSize") within a function:
function Size(isSize) {
this.isSize = 80;
setSize(this.isSize);
}
And here's the second variable of which I want to assign the previous variable to:
var sizeObject;
I've been trying various ways such as the following:
function createSize(isSize){
var isSize = new sizeObject();
}
Anyone got any ideas? Many thanks
If I understand your comment correctly, here is what you are looking for:
// this is the constructor of the `Size` class
function Size() {
this.isSize = 80;
}
function createSize() {
// add this line:
var sizeObject = new Size();
}
It's very hard to understand quite what you're asking, but this:
var sizeObject = new Size(20);
...will create a new object with a isSize property on it with the value 20 if you change Size to:
function Size(initialSize) {
this.isSize = initialSize;
}
(E.g., so it actually uses the argument you give it, rather than using an hardcoded 80.)
Size in the above is a constructor function. You use constructor functions via new.
Weird problem here, I'm trying to use a global function to update my settings object, example:
var Settings = new Object;
Settings.savepos = 'true';
function UpdateSetting(obj,value){
eval("Settings.obj = value");
alert(Settings.savepos);
}
The obj is the key of the object, meaning if I call the function with
UpdateSetting('savepos','false')
the alert will always just give me true, how do I convert that eval or any alternative so it will update settings object's key with the value?
You are setting Settings.obj, not setting.savepos.
Try this instead:
function UpdateSetting(obj,value){
Settings[obj] = value;
alert(Settings.savepos);
};
You are always changing the "obj" key of the object to equal value, which is likely to be undefined (or, at least, not defined to what you want) in the context eval() executes it in. So, you have two options. First, you can keep using eval() (although i don't recommend it because it's more pain than necessary):
var Settings = new Object;
Settings.savepos = 'true';
function UpdateSetting(obj,value){
eval("Settings."+obj+" = '"+value+"'");
alert(Settings.savepos);
}
Or, as numerous other have suggested, you can use the array operator[] to access the property by key:
var Settings = new Object;
Settings.savepos = 'true';
function UpdateSetting(obj,value){
Settings[obj] = value;
alert(Settings.savepos);
}
you dont need an eval
you're setting .obj, not .savepos (there is no interpolation for the string)
you may be calling it wrong.
I'm not exactly sure why you don't just set the value directly (eg. Settings.savepos=false;).
You can attach the function to that object to do something similar:
var Settings = new Object;
Settings.savepos = true;
Settings.UpdateSetting = function (prop,value){this[prop] = value;}
Settings.UpdateSetting('savepos',false);
You should be able to use array notation on the object. Underneath it's just a keyed hash.
Try:
Settings[obj] = value;
I'd also suggest passing values as they are, i.e. string, int, etc:
UpdateSetting('key_name', false);