How to preserver object scope while using destructuring assignment? - javascript

Consider the following code:
const obj = {
a: 'foo',
b: function () { return this.a }
}
console.log(obj.a) // 'foo'
console.log(obj.b()) // 'foo'
const { a, b } = obj
console.log(a) // 'foo'
console.log(b()) // undefined
When I destruct obj, b() cannot access a anymore.
How can I preserve obj scope and allow b() to access it's siblings properties/methods while descructuring?

#dayan-moreno-leon posted a good option.
But I just noticed that I can be handled simply by using always static calls:
const obj = {
a: 'foo',
b: function () { return obj.a } //HERE! :D
}
const { a, b } = obj
console.log(obj.a) // 'foo'
console.log(obj.b()) // 'foo'
console.log(a) // 'foo'
console.log(b()) // 'foo'

Related

assignment to destructured object property from function parameter list doesn't mutate object

is this expected?
const testObj = {
a: 'hi',
b: 'there'
}
function destructured_test({a, b}) {
a = 'bye';
}
console.log(testObj);
destructured_test(testObj);
console.log(testObj);
function test(t) {
t.a = 'bye';
}
console.log(testObj);
test(testObj);
console.log(testObj);
only the function 'test' mutates testObj 'destructured_test' seems to be copying the properties over instead of preserving the reference

destructing an object being function's argument with default property

I want to pass an object as an argument to the function. If property 'prop' of this object is undefined, it should be initialized by default value.
How to do it using modern JS?
I expect something like this:
const fun = function(options = { prop: 'default'}) { console.log(options.prop)}
fun({a: 'x', prop: 'hello'}) ... // log: 'hello'
fun({a: 'x'}) ... // log: 'default'
If you want to destructure in the parameter list, you won't have access to the entire original object (the options, here) anymore - you'll only have the destructured variables. So, leave out the options = part, and put the = after the prop, instead of :. For example:
const fun = function({ prop = 'default', a }) {
console.log('prop:', prop, 'a:', a);
};
fun({
a: 'x',
prop: 'hello'
}) // log: 'hello'
fun({
a: 'x'
}) // log: 'default'
If the function also may not be called with any parameters, you may default-assign an empty object:
const fun = function({ prop = 'default', a } = {}) {
console.log('prop:', prop, 'a:', a);
};
fun({
a: 'x',
prop: 'hello'
}) // log: 'hello'
fun({
a: 'x'
}) // log: 'default'
fun(); // log: 'default'
Use this syntax instead:
const fun = ({ prop = "default" }) => console.log(prop);
fun({ a: "x", prop: "hello" });
fun({ a: "x" });
Note that you will lose the object, and only have access to prop. To solve this, you can collect the rest of the properties and expand them into an object:
const fun = ({ prop = "default", ...rest }) => {
console.log(prop);
const otherProps = { ...rest };
console.log(JSON.stringify(otherProps));
}
fun({ a: "x", prop: "hello" });
fun({ a: "x" });
fun({ a: "x", b: "nonexistent", c: true, prop: "Hi!" });
You could use the rest properties and exclude prop from the object, take the value or default and assign this value back to the object.
const
fun = function({ prop = 'default', ...options }) {
Object.assign(options, { prop });
console.log(options.prop);
};
fun({ a: 42 });
fun({ a: 42, prop: 'bar' });
I think you have to handle this inside the function itself. Below is the code which will work for your particular scenario
var fun = function(options = { prop:'default'}) {
if(options.prop == undefined){
options.prop = 'default'
}
console.log(options.prop)
}
fun({a:'hello'})// logs: default

how to object value to its on property object?

how to object value to its on property object ?
I tried like this
var obj = {
a:2,
b:this.a
}
Then try obj.b it is giving undefined ..can I make function in object?
Expected output 2
const obj = {
a: 2,
get b() {
return this.a;
}
};
console.log(obj.b);
const obj2 = {
a: 2,
b: function() {
return this.a;
}
};
console.log(obj2.b());
See it in action: https://jsfiddle.net/rqnbxw86/

Destruct object with dynamic parameters

I'm trying to destruct object by passing params, but i can't achieve it the way i want. Param's are string's.
function(param){
const obj = {
foo: 'foo',
bar: 'bar'
}
const {[param], ...destructedObj} = obj; // where param === 'foo' || 'bar'
return obj;
}
You can use Object.keys to retrieve keys and then decide which key to use to selectively destruct the object at hand:
const obj = {
foo: 1,
bar: 2
};
const keys = Object.keys(obj);
const { [keys[0]]: x } = obj;
console.log(`${keys[0]}:${x}`);
But I'm not sure where you are going with this?

Destructuring objects as function parameters deep extend

Is it possible to "deep extend" objects when using destructuring to set default properties of an object passed into a function?
Example:
function foo({
foo = 'foo',
bar = 'bar',
baz = {
propA: 'propA',
propB: 'propB'
}
} = {}) {
console.log(foo);
console.log(bar);
console.log(baz);
}
foo({
foo: 'changed',
baz: {
propA: 'changed'
}
});
This outputs: (baz is overwrote)
changed
bar
{
"propA": "changed"
}
Is there syntax which will extend the baz object, to give output:
changed
bar
{
"propA": "changed",
"propB": "propB"
}
There is no native-way to do what you ask, but you can write your own function decorator, of course it is a verbose approach...
Note: In order to perform a deep merge you should use some of the existing solution developed by the community, both Object.assign and Object Spread Operator perform a shallow copy of the source, even, writing your own deepmerge function could be error-prone. I suggest you to use lodash#merge that is commonly accepted as one of the solid solutions to the problem.
var {
// https://lodash.com/docs/#merge
merge
} = require('lodash');
function defaultsArgDecorator(defaults, target) {
return function(args) {
return target(
merge(Object.create(defaults), args)
)
}
}
function _foo(args) { console.log({args}); }
var foo = defaultsArgDecorator({
foo: 'foo',
bar: 'bar',
baz: {
propA: 'propA',
propB: 'propB'
}
}, _foo);
foo({bar: "ciao bello", baz: {propA: "HELLO"}})
Since parameters destructuring allow you to use previous parameters in the defaults of other parameters, you can create a property that doesn't exit on the original parameters, for example __baz, and set its defaults using baz.
In the method you'll use __baz instead of baz.
Note: this is a hack, and if the object contains a property by the
name of __baz it will override the default, with unexpected results.
However, you can name the default property with something like
dontUse__baz, which has a very low chance of being used.
Default properties using Object#assign:
function foo({
foo = 'foo',
bar = 'bar',
baz,
__baz = Object.assign({
"propA": "changed",
"propB": "propB"
}, baz)
} = {}) {
console.log(foo);
console.log(bar);
console.log(__baz);
}
foo({
foo: 'changed',
baz: {
propA: 'changed'
}
});
Default properties using object spread (requires babel plugin - see link):
function foo({
foo = 'foo',
bar = 'bar',
baz,
__baz = {
"propA": "changed",
"propB": "propB",
...baz
}
} = {}) {
console.log(foo);
console.log(bar);
console.log(__baz);
}
foo({
foo: 'changed',
baz: {
propA: 'changed'
}
});
You can also do nested destructuring. You destructure baz, but also destructure propA and propB.
If you need your object whole and don't want to 'rebuild' it, this isn't for you, but it does accomplish one objective of setting default values for unspecified properties nested in an object.
I've left baz as a separate descructured value in addition to it's properties as an example of a sometimes handy pattern, but you would most likely want to remove it since it suffers from the same problem of losing default properties.
function foo({
foo = 'foo',
bar = 'bar',
baz, // likely extraneous and confusing, here for example
baz: { propA = 'propA', propB = 'propB' }
} = {}) {
console.log(foo);
console.log(bar);
console.log(propA, propB);
console.log(baz);
console.log(Object.assign(baz, {propA, propB}));
}
foo({
foo: 'changed',
baz: {
propA: 'changed'
}
});
Yep, totally possible!
Solution 1:
function foo({
foo = 'foo',
bar = 'bar',
baz: {
propA = 'propA',
propB = 'propB'
} = {}
}) {
console.log(foo)
console.log(bar)
console.log({propA, propB}) // Because `bar` is renamed to `{propA, propB}`. Hence, no local variable named `baz`
}
Solution 2 (Workaround):
function foo({
foo = 'foo',
bar = 'bar',
baz = {}
}) {
// ...
const baz2 = Object.assign({}, {propA: 'propA', propB: 'propB'} = {})
console.log(baz2) // Yeah, not really a solution
}
You can even do this:
const func = ([{foo, bar: {a, b: BB, c: CC = 'c'} = {}} = {}], ...{0: rest0 = 0, 1: rest1 = '1'}) => { /* do stuffs */ }
The code above is a complicated example, I'll use simpler examples to explain:
Example 1:
const f1 = ({x: varx}) => console.log(varx)
When you call f1(obj), obj.x will be assigned to varx in f1's scope
Example 2:
const f2 = ({xy: {x, y}}) => console.log({x, y})
Similar to f1 but with {x, y} in place of varx, obj.xy will be assigned to {x, y}, so x = obj.xy.x and y = obj.xy.y
Example 3: Now let add create default parameters
const f3 = ({xy: {x, y} = {}}) => { /* do stuffs */ }
Now, you still have to pass obj as an object but you no longer have to provide obj.xy. If obj.xy is undefined, x and y are undefineds
Example 4: Now, let x and y have default values
const f4 = ({xy: {x = 'XXX', y = 'YYY'} = {}}) => { /* do stuffs */ }
If obj.xy you passed is undefined, {x, y} will be set to {x: undefined, y: undefined}, but since x and y also have default values, x will be 'XXX' and y will be 'YYY'
Example 4: "THE ULTIMATE LIFE FORM"
const f5 = ({xy: {x = 'XXX', y = 'YYY'} = {}} = {}) => { /* do stuffs */ }
Guess I don't have to explain this anymore :D

Categories