According to ES6, it is possible to use array destructuring returned from custom generators. Now however I couldn't figure a way to do the same with simple object destructuring as I thought this should work (using Babel and Polyfill):
class Test {
constructor () { this.props = {a: 1, b: 2}; }
*[Symbol.iterator]() { yield* this.props; }
}
const {a, b} = new Test();
This should actually work no? Because it always simply returns undefined for a and b.
Array destructuring is tied to iterable/iterator objects. Object destructuring does not. If you want
const {a, b} = foo
to pull values for a and b, then foo needs to actually return a value for foo.a and foo.b. So you'd either want to just store the properties on the instance itself instead of .props, or use a getter like
get a() {
return this.props.a;
}
According to ES6, it is possible to use array destructuring returned from custom generators.
Yes.
This should actually work?
No - you were not using arrays here. By destructuring onto an object literal, the iterator was never invoked, and if it was it would have thrown an exception about the this.props object not being iterable.
Now however I couldn't figure a way to do the same with simple object destructuring as I thought this should work (using Babel and Polyfill):
class Test {
constructor () {
this.propA = 1;
this.propB = 2;
this.propArr = [];
}
*[Symbol.iterator]() {
yield this.propA; // yield the single value
yield this.propB; // yield another value
yield* this.propArr; // yield every from an iterable
}
}
const [a, b] = new Test();
// ^ ^ *array* destructuring!
Object destructuring could be used here as
const {propA: a, propB: b} = new Test();
Related
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
Is there any difference between the following two examples:
const nodes = document.querySelectorAll('.someclass');
for(const node of nodes){
// do something
}
and
for(const node of document.querySelectorAll('.someclass')){
// do something
}
Does the function get re-invoked on every iteration? Or are these exactly the same besides the syntax?
They're exactly the same, except for the extra nodes variable name. When the interpreter sees
for (const varName of <expression>)
it will first evaluate expression and then try to iterate over it. It only evaluates that expression once, in an attempt to get an iterable out of it.
const getIterable = function* () {
console.log('getIterable running');
yield 'foo';
yield 'bar';
yield 'baz';
};
for (const item of getIterable()) {
console.log(item);
}
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 want to use Object.assign to clone an instance of a class, including any methods along with it. Is it possible to do with just Object.assign or should I be using something like lodash deepClone?
class Foo {
constructor() {
this.a = 1;
this.b = 2;
}
add() {
return this.a + this.b;
}
}
const foo1 = new Foo();
console.log(foo1.add());
console.log(foo1.b);
// ? Where did the add go?
const foo2 = Object.assign({}, foo1, { b: 99 });
console.log(foo2.add());
console.log(foo2.b);
Example -
http://jsbin.com/dacopazanu/edit?js,console
Object.assign only copies own enumerable properties, which inherited class methods are not.
But you wouldn't want to copy those anyway - to clone an instance, you want to create an object that inherits from the same prototype:
const foo2 = Object.assign(Object.create(Object.getPrototypeOf(foo1)), foo1);
It appears the answer is, that out of the box, no it's not possible.
Using helpful utility libraries like lodash and in particular it's cloneDeep method seems to be the sure way to go.