I have a module to set config. It's working. I want to get config using same module. I don't want to use additional file.
I tried a code something like that:
let config_defaults = { test_option: false };
let active_config;
if(active_config === undefined){ active_config = config_defaults };
exports.getcfg = active_config;
const setcfg function(options){
options = Object.assign({}, config_defaults, options);
active_config = options;
return(options);
}; exports.setcfg = select_config;
setcfg returns changes with no problem. But I can't get changes at getcfg. Is it possible to get changes without using additional file?
Outputs:
console.log( testmodule.setcfg({test_option: true}) ); /* >> Output: { test_option: true } */
console.log( testmodule.getcfg ); /* >> Output: { test_option: false } */
You can totally replicate the Java getter/setter behaviour with JavaScript class:
const config_defaults = {
test_option: false
};
/*export*/ class ConfigManager {
activeConfig = config_defaults;
getActiveConfig() {
return this.activeConfig;
}
setActiveConfig(options) {
options = Object.assign({}, config_defaults, options);
this.activeConfig = options;
return options;
}
}
// import { ConfigManager } from "./testmodule";
const configManager = new ConfigManager();
console.log(configManager.setActiveConfig({
test_option: true
})); // >> Output: { test_option: true }
console.log(configManager.getActiveConfig()); // >> Output: { test_option: true }
With JavaScript, you do not necessarily need to use a class, especially for a singleton; in this case, an object is enough:
const config_defaults = {
test_option: false
};
/*export*/ const configManager = {
activeConfig: config_defaults,
getActiveConfig() {
return this.activeConfig;
},
setActiveConfig(options) {
options = Object.assign({}, config_defaults, options);
this.activeConfig = options;
return options;
}
};
// import { configManager } from "./testmodule";
console.log(configManager.setActiveConfig({
test_option: true
})); // >> Output: { test_option: true }
console.log(configManager.getActiveConfig()); // >> Output: { test_option: true }
Still with JavaScript, you can even hide the getter/setter pattern:
const config_defaults = {
test_option: false
};
/*export*/ const configManager = {
_privateActiveConfig: config_defaults,
get activeConfig() {
return this._privateActiveConfig;
},
set activeConfig(options) {
options = Object.assign({}, config_defaults, options);
this._privateActiveConfig = options;
return options;
}
};
// import { configManager } from "./testModule";
console.log(configManager.activeConfig = {
test_option: true
}); // >> Output: { test_option: true }
console.log(configManager.activeConfig); // >> Output: { test_option: true }
The cause of the issue is that you already assign the reference to an active_config object when defining exports.getcfg, but later in setcfg, active_config is re-assigned a reference to
a different object.
You could compare the assignment to an object reference as writing down the address of that object in a variable. If you re-assign active_config, all other variables that knew its previous address still reference that old content.
See e.g. Is JavaScript a pass-by-reference or pass-by-value language?
You have 2 easy solutions:
Perform a deep mutation: instead of re-assigning the content of active_config directly, you change the value of one of its members, as if it was a "routing switch"
Make the getter a function, which can then use the most recent version of the scope variable when executed
const exports = testmodule = {};
const config_defaults = {
test_option: false
};
// Option 1: expose a switch instead of directly the changing variable
const active_config_switch = {
active_config: config_defaults,
};
exports.getcfg_switch = active_config_switch;
// Option 2: getter as a function
let active_config = config_defaults;
exports.getcfgFn = function() {
return active_config;
}
const select_config = function(options) {
options = Object.assign({}, config_defaults, options);
active_config_switch.active_config = options; // Option 1: deep mutation
active_config = options;
return options;
};
exports.setcfg = select_config;
console.log(testmodule.setcfg({
test_option: true
})); /* >> Output: { test_option: true } */
// Option 1: read the deep member
console.log(testmodule.getcfg_switch.active_config); /* >> Output: { test_option: true } */
// Option 2: execute the getter function
console.log(testmodule.getcfgFn()); /* >> Output: { test_option: true } */
Related
Example
Json
let Settings = {
"Policy": {
"allowP": true,
"allowC": false,
}
}
How can i access get value using following syntax
console.log( Policy.allowP ); // should return true
console.log( Policy.value.allowP ); // should also return true
i tried following using javascript proxy
let createProxy = (settings : any) => {
const p = new Proxy(settings, {
get(target, p, _r) {
debugger;
if (p === "value") {
return settings;
}
return (target as any)[p];
},
});
return p;
}
let getSettings = () => {
var settings = Settings;
const p = new Proxy(settings, {
get(target, p, _r) {
debugger;
if (p === "value") {
return createProxy(settings);
}
return createProxy((target as any)[p]);
},
});
return p;
};
Tried to create Nested proxy so that i can access values using key.value syntax.
Is this correct way to implement it ? or is there any better way ?
I recently learned about composing objects together using functions from reading this article. Following along, I end up with this code:
function withFlying(o) {
let _isFlying = false;
return {
...o,
fly () {
_isFlying = true;
},
land () {
_isFlying = false;
},
isFlying () {
return _isFlying
}
}
};
function withWalking(o) {
let isWalking = false;
return {
...o,
startWalking() {
isWalking = true;
return this
},
stopWalking() {
isWalking = false;
return this
},
isWalking: () => isWalking
}
}
const bird = withWalking(withFlying({}))
Everything here works. However, I would like to be able to call isFlying as a property instead of a function:
// current (working)
bird.isFlying() // return value of `_isFlying`
// desired
bird.isFlying // return value of `_isFlying`
I know that get and set are keywords that can be used in object literals, and so I tried this:
function withFlying(o) {
let _isFlying = false
return {
...
get isFlying () {
return _isFlying
}
}
}
But it doesn't show the correct value after updating using the other functions. I figured that with the get property being a function, closures would apply similar to the other functions. Am I wrong in this assumption? Is there underlying behavior with get that I'm not understanding, and is what I'm trying to achieve possible the way I'm doing it now?
Here's a snippet with the code I tried to use:
function withFlying(o) {
let _isFlying = false;
return {
...o,
fly () {
_isFlying = true;
},
land () {
_isFlying = false;
},
valueOf_isFlying() {
return _isFlying;
},
get isFlying () {
return _isFlying
}
}
};
function withWalking(o) {
let isWalking = false;
return {
...o,
startWalking() {
isWalking = true;
return this
},
stopWalking() {
isWalking = false;
return this
},
isWalking: () => isWalking
}
}
const bird = withWalking(withFlying({}))
// desired
console.log(bird.isFlying) // _isFlying starts false
bird.fly() // should set _isFlying to true
console.log(bird.isFlying) // still returns false
console.log(bird.valueOf_isFlying()) // shows _isFlying is true
The problem is that when you create your new object, you're using spread notation to copy the properties from the original object:
return {
...o,
// ...
};
The problem with that is it copies the then-current value of accessor properties, not the definition of the accessor property. You can see that here:
const obj1 = {
get example() {
return 42;
}
};
console.log("Notice that the property descriptor is for an accessor property:");
console.log(Object.getOwnPropertyDescriptor(obj1, "example"));
const obj2 = {...obj1};
console.log("Notice that the property descriptor is for a simple data property:");
console.log(Object.getOwnPropertyDescriptor(obj2, "example"));
.as-console-wrapper {
max-height: 100% !important;
}
It's very much as though you did:
for (const key of Object.keys(o) {
newObject[key] = e[key];
}
e[key] gets the then-current value of the property, not the definition of the property.
To fix it, use Object.getOwnPropertyDesciptors to get the descriptors of the properties, and use Object.defineProperties to define those same properties on the new object. Since you're doing that (and adding more properties) in at least two places, you probably want a utility function:
function assignPropertyDescriptors(target, obj, updates) {
// B
return Object.defineProperties(
// A
Object.defineProperties(
target,
Object.getOwnPropertyDescriptors(obj)
),
Object.getOwnPropertyDescriptors(updates)
);
}
The "A" Object.defineProperties call copies the original object's property descriptors and applies them to the new object. The "B" Object.defineProperties call applies the ones you're adding to that new object as well.
But let's generalize that into a loop, similar to Object.assign (hence the name assignPropertyDescriptors):
function assignPropertyDescriptors(target, ...updates) {
for (const update of updates) {
Object.defineProperties(
target,
Object.getOwnPropertyDescriptors(update)
);
}
return target;
}
withFlying and withWalking would then use that worker function, for instance:
function withFlying(o) {
let _isFlying = false;
return assignPropertyDescriptors({}, o, {
fly () {
_isFlying = true;
},
land () {
_isFlying = false;
},
get isFlying () {
return _isFlying
}
});
};
Here's a complete example:
function assignPropertyDescriptors(target, ...updates) {
for (const update of updates) {
Object.defineProperties(
target,
Object.getOwnPropertyDescriptors(update)
);
}
return target;
}
function withFlying(o) {
let _isFlying = false;
return assignPropertyDescriptors({}, o, {
fly () {
_isFlying = true;
},
land () {
_isFlying = false;
},
get isFlying () {
return _isFlying
}
});
};
function withWalking(o) {
let isWalking = false;
return assignPropertyDescriptors({}, o, {
startWalking() {
isWalking = true;
return this
},
stopWalking() {
isWalking = false;
return this
},
isWalking: () => isWalking
});
}
const bird = withWalking(withFlying({}))
console.log(bird.isFlying) // _isFlying starts false
bird.fly() // should set _isFlying to true
console.log(bird.isFlying) // _isFlying is true
The code below works. Is there a way that is more convenient, if possible even a one-liner?
const { nextUrl, posts } = await postService.getCommunityPosts(6);
this.communityPosts = posts;
this.nextUrl = nextUrl;
I know about giving destructured properties aliases but I don't think that helps in this case. MDN doesn't say anything about that case.
You can assign to the properties of an existing object by giving aliases and encapsulating the assignment in parentheses (await codepen).
const demo = { nextUrl: 'nextUrl', posts: 'posts' };
const target = {}; // replace target with this
({ nextUrl: target.nextUrl, posts: target.communityPosts } = demo);
console.log(target);
function Person() {
this.obj = {
firstName: 'Dav',
lastName: 'P'
};
({firstName: this.firstName, lastName: this.lastName} = this.obj);
}
let p = new Person();
console.log(p);
An alternative that doesn't require duplicate property keys that ({key1: this.key1, key2: this.key2} = ... does is to use Object.assign().
class X {
constructor(properties) {
({...this} = properties); // Invalid destructuring assignment target
}
}
x = new X({a: 3});
console.log(x);
class X {
constructor(properties) {
Object.assign(this, properties);
}
}
x = new X({a: 3});
console.log(x);
Recently, I read a js file, there's a destructure like below,
IMO, I would write const { files } = obj.props; , but
what does const { file=[] } = obj.props mean?
why write like this and what's the benefits of this?
const obj = {
props: {
files: [1, 2, 3],
},
};
const { files = [] } = obj.props;
// const { files } = obj.props;
console.log(files);
Default values
A variable can be assigned a default, in the case that the value unpacked from the array is undefined.
Source: MDN
const obj = {
props: { not_files: 1 },
};
const { files = ["default", "value"] } = obj.props;
console.log(files);
It takes the empty array as default value for the missing property. More under default values or default parameters.
const obj = { props: {} };
const { files = [] } = obj.props;
console.log(files);
In this code:
function Cls() {
this._id = 0;
Object.defineProperty(this, 'id', {
get: function() {
return this._id;
},
set: function(id) {
this._id = id;
},
enumerable: true
});
};
var obj = new Cls();
obj.id = 123;
console.log(obj);
console.log(obj.id);
I would like to get { _id: 123, id: 123 }
but instead I get { _id: 123, id: [Getter/Setter] }
Is there a way to have the getter value be used by the console.log function?
You can use console.log(Object.assign({}, obj));
Use console.log(JSON.stringify(obj));
Since Nodejs v11.5.0 you can set getters: true in the util.inspect options. See here for docs.
getters <boolean> | <string> If set to true, getters are inspected. If set to 'get', only getters without a corresponding setter are inspected. If set to 'set', only getters with a corresponding setter are inspected. This might cause side effects depending on the getter function. Default: false.
You can define an inspect method on your object, and export the properties you are interested in. See docs here: https://nodejs.org/api/util.html#util_custom_inspection_functions_on_objects
I guess it would look something like:
function Cls() {
this._id = 0;
Object.defineProperty(this, 'id', {
get: function() {
return this._id;
},
set: function(id) {
this._id = id;
},
enumerable: true
});
};
Cls.prototype.inspect = function(depth, options) {
return `{ 'id': ${this._id} }`
}
var obj = new Cls();
obj.id = 123;
console.log(obj);
console.log(obj.id);
I needed a pretty printed object without the getters and setters yet plain JSON produced garbage. For me as the JSON string was just too long after feeding JSON.stringify() a particularly big and nested object. I wanted it to look like and behave like a plain stringified object in the console. So I just parsed it again:
JSON.parse(JSON.stringify(largeObject))
There. If you have a simpler method, let me know.
On Node.js, I suggest using util.inspect.custom, which will allow you to pretty print getters as values, while keeping other properties output unchanged.
It will apply to your specific object only and won't mess the general console.log output.
The main benefit vs Object.assign is that it happens on your object, so you keep the regular generic console.log(object) syntax. You don't have to wrap it with console.log(Object.assign({}, object)).
Add the following method to your object:
[util.inspect.custom](depth, options) {
const getters = Object.keys(this);
/*
for getters set on prototype, use instead:
const prototype = Object.getPrototypeOf(this);
const getters = Object.keys(prototype);
*/
const properties = getters.map((getter) => [getter, this[getter]]);
const defined = properties.filter(([, value]) => value !== undefined);
const plain = Object.fromEntries(defined);
const object = Object.create(this, Object.getOwnPropertyDescriptors(plain));
// disable custom after the object has been processed once to avoid infinite looping
Object.defineProperty(object, util.inspect.custom, {});
return util.inspect(object, {
...options,
depth: options.depth === null ? null : options.depth - 1,
});
}
Here is a working example in your context:
const util = require('util');
function Cls() {
this._id = 0;
Object.defineProperty(this, 'id', {
get: function() {
return this._id;
},
set: function(id) {
this._id = id;
},
enumerable: true
});
this[util.inspect.custom] = function(depth, options) {
const getters = Object.keys(this);
/*
for getters set on prototype, use instead:
const prototype = Object.getPrototypeOf(this);
const getters = Object.keys(prototype);
*/
const properties = getters.map((getter) => [getter, this[getter]]);
const defined = properties.filter(([, value]) => value !== undefined);
const plain = Object.fromEntries(defined);
const object = Object.create(this, Object.getOwnPropertyDescriptors(plain));
// disable custom after the object has been processed once to avoid infinite looping
Object.defineProperty(object, util.inspect.custom, {});
return util.inspect(object, {
...options,
depth: options.depth === null ? null : options.depth - 1,
});
}
};
var obj = new Cls();
obj.id = 123;
console.log(obj);
console.log(obj.id);
Output:
Cls { _id: 123, id: 123 }
123
Use spread operator:
console.log({ ... obj });