I have an object which I initialize with bunch of properties in node.js, spreading another object in, and at the end defining a getter.
The problem is the the getter uses this and all the properties defined on the object before the spread don't exist on this.
If I move the spread to the end or start of the object initialization, it works fine.
const obj = { a: 1 }
const obj2 = {
prop: 'a',
...obj,
prop2: 'b',
get test() {
return this.prop + this.prop2
}
}
console.log(obj2.test); // 'undefinedb'
What can possibly be the reason for that?
Thanks
Related
Previously I thought that object's getter will be computed if and only if will be explicitly called:
const obj = {
log: ['a', 'b', 'c'],
get latest() {
console.log("checkpoint1")
if (this.log.length === 0) {
return undefined;
}
return this.log[this.log.length - 1];
}
};
console.log(obj.latest); // called!
However if we try to print the obj in console
console.log(obj)
checkpoint1 from 4th row will also printed! But I noticed this phenomena when run some UI tests without explicit calling of getters, so the printing of object in console on not only one case when getters could be implicitly called.
By why getter has been called?
This is a bit of a tricky question regarding ES6 destructuring with default usage in a javascript object constructor.
I would like to receive a destructured parameters object with default values for my object constructor
so what i did was this
function bla({a=3,b=6}={}){
this.a=a;
this.b=b;
console.log(`this.a::'${this.a}' this.b::'${this.b}' a::'${a}' b::'${b}'`);
}
let myObject= new bla({a:1});
console.log(`myObject.a::'${myObject.a}' myObject.b::'${myObject.b}'`); // only a got overriden with value "1" and b remained its defauly value "6"
I know that what i did works. However, you can see that this is a bit of a code smell, because every time i need to add a new parameter to the constructor (for example {newParameter=3}) i also need to go down and add a matching line like this in the constructor body
this.newParameter=newParameter;
Is there any more elegant way to add a destructured parameter with default value which automatically is attached to "this."
I personally think your current approach is the most readable, but you can technically also do
function bla(obj = {}){
({
a: this.a = 3,
b: this.b = 6,
} = obj);
console.log(`this.a::'${this.a}' this.b::'${this.b}'`);
}
Don't know if this is best, but you could use Object.assign.
function bla(props={}) {
Object.assign(this, {a:3,b:6}, props);
console.log(`this.a::'${this.a}' this.b::'${this.b}'`);
}
let myObject= new bla({a:1});
console.log(`myObject.a::'${myObject.a}' myObject.b::'${myObject.b}'`); // only a got overriden with value "1" and b remained its defauly value "6"
You lose the parameter names, but it could be argued that this is better so that you don't accidentally mutate the parameter when you meant to mutate the object property.
I follow more a functional style to avoid the new on object creation and this keyword.
Explanation
You could simply write a function that retuns an object and takes several arguments with default values.
Thanks of the object literal property value shorthand you have less to write.
Code
function returnObject(a = 1, b = 2, c = 3) {
return {
a,
b,
c
}
}
console.log(returnObject())
If the objective is to use the variable names exactly once then this is what I'd do:
const foo = new Foo({ a: 1 });
console.log(`foo { a: ${foo.a}, b: ${foo.b} }`);
function Foo(config = {}) {
const defaults = { a: 3, b: 6 };
for (const [key, val] of Object.entries(defaults))
({ [key]: this[key] = val } = config);
}
Now, you only need to update the defaults object and you're done.
Actually, let's make a constructor constructor by abstracting this pattern:
const Foo = defcons({ a: 3, b: 6 });
const foo = new Foo({ a: 1 });
console.log(`foo { a: ${foo.a}, b: ${foo.b} }`);
function defcons(defaults) {
return function (config = {}) {
for (const [key, val] of Object.entries(defaults))
({ [key]: this[key] = val } = config);
};
}
Now, you can easily create as many such constructors as you want.
I am currently using the object destructuring pattern with default parameters described in that answer to ES6 Object Destructuring Default Parameters.
(function test({a = "foo", b = "bar"} = {}) {
console.log(a + " " + b);
})();
I would like to be able to access the object parameter without assigning it to a variable in the function body and explicitly listing each key.
(function test({a = "foo", b = "bar"} = {}) {
const options = {a, b};
console.log(options);
})();
I tried naming the object argument, but the function looses the ability to resolve missing keys to their default value.
(function test(options = {a = "foo", b = "bar"} = {}) {
console.log(options);
})();
It seems to be ignoring the default parameters when destructuring into a named argument.
Is this part of the ES6 spec? Is there a way to achieve the desired behavior without additional code in the function body?
Edit: I removed a superfluous example that did not add context to the question.
Honestly, I think you're overcomplicating this. Default parameters are not compulsory -- in this case your code can be cleaner without it.
I would simply take the object options as the parameter and do the destructuring within the body of the function, after assigning default values.
function test(options) {
options = Object.assign({a: 'foo', b: 'bar'}, options);
let {a, b} = options;
console.log(options, a, b);
}
test(); // foo bar
test({a: 'baz'}); // baz bar
test({b: 'fuz'}); // foo fuz
test({c: 'fiz'}); // foo bar
With particular regard to your final snippet:
(function test(options = {a: "foo", b: "bar"}) {
console.log(options);
})({a: "baz"});
The problem is that a default parameter is used when the value passed is undefined. Here, the value passed is {a: "baz"}. That is not undefined, so the default parameter is ignored. Objects are not merged automatically.
More broadly in answer to your question: there is no way of getting both an object and destructuring some of its properties in the parameters of a method. Frankly, I'm grateful, because function signatures can be hard enough to read at first glance as it is.
i have problem with assigning property to a object created with constructor INSIDE other function
var something = {
property1: 'sth',
object: new Object()
}
and it works it creates object inside a object, but everytime i try to assign property to object inside object like
var something = {
property1: 'sth',
object: new Object(),
object.property: 'anything'
}
it shows error, Uncaught syntaxError: unexpected token . , albo tried with object['property'] but shows "unexpected token [", so how do i assign it ?
You can do it in two steps:
var something = {
property1: 'sth',
object: new Object(),
};
something.object.property = 'anything';
Or use Object.assign to merge properties into the created object:
var something = {
property1: 'sth',
object: Object.assign(new Object(), {
property: 'anything',
}),
};
Of course if you are the one creating the object (as opposed to some third party function creating the object), there is no need to use new Object. Just use an object literal as well.
function myConstructor (arg) {
this.myName = arg;
this.totalNumber = 0;
this.foo = {
bar: {
someBoolean: false,
someNumber: 5
},
baz: {
someBoolean: false,
someNumber: 10
}
};
}
myConstructor.prototype.getNumber = function () {
console.log(this); //successfully returns the child object
for (var i in this.foo) {
//console log tests
console.log(this); //still returns the child object with all properties, including the myName 'whatever'
console.log(this.foo); //returns the 'foo' object with all nested properties
console.log(i); //returns 'bar' and 'baz', respectively
console.log(this.foo.hasOwnProperty(i)); //returns true
//where it all goes wrong
console.log(typeof(i)); //returns 'string'
console.log(this.foo.i); //returns undefined, even though 'this.foo' definitely has 'bar' and 'baz' properties
//what I'm trying to accomplish
/*
if (this.foo.i.hasOwnProperty('someBoolean') && this.foo.i.someBoolean === true) {
this.totalNumber += this.foo.i.someNumber;
} //returns 'TypeError: Cannot read property 'hasOwnProperty' of undefined
*/
}
return this.totalNumber;
};
var myChild = new myConstructor('whatever');
myChild.getNumber();
What I'm trying to accomplish is using a constructor to create a child. The having nested objects inside that child, with various properties that I will change later in my code. Then using a method of the constructor to access data within the nested objects of that child. Everything works until I get two-deep in nested objects.
I've tried passing every variable, object and property around with various "var this == that"s and "var prop == i"s and etc. Nothing I do seems to work.
foo has no property named i.
You want foo[i], to get the property with that name.
it should be console.log(this.foo[i])
As foo doen not contain "i" property.
Your confusion lies in the way that for-each/for-in loops are normally used in other programming languages such as Java, C#. Here's the difference:
// java
for(int x in list)
x = x+1;
// javascript
var x;
for(x in list)
list[x] = list[x] + 1;