ES6 pass an object to constructor and set property - javascript

Look at the below example:
class Parent {
constructor({ parentOnlyArg = 'default value' } = {}) {
this.parentOnlyArg = parentOnlyArg;
}
}
class Child extends Parent {
// this class and also any class inheriting from it
constructor({ visibleStyle = 'inline' } = {}) {
// I want to pass argument to super as an object
super(/** args **/);
this.visibleStyle = visibleStyle;
}
}
class Child2 extends Parent {
// Specifying parentOnlyArg as default will change the behaviour
constructor({ parentOnlyArg = 'another parent value',
someOther = 'value' } = {}) {
// I want to pass argument to super as an object
super(/** args **/);
this.someOther = someOther;
}
}
Is it possible to pass on the constructor argument to super?
Seems like it was simpler than I thought
super(...arguments);
I can then create Child using
var c1 = new Child(); // c.parentOnlyArg = 'default value'
var c2 = new Child2(); // c.parentOnlyArg = 'another parent value'
var c3 = new Child({ parentOnlyArg: 'changed again' }); // c.parentOnlyArg = 'changed again'

You could use object destructuring with rest properties. It is not yet implemented by browsers, but BabelJs can transpile it.
function assertEmpty(obj) {
if (Object.keys(obj).length > 0) {
throw new Error("Unexpected parameters");
}
}
class A {
constructor({ a = "foo", ...rest } = {}) {
assertEmpty(rest);
console.log("new A " + a);
}
}
class B extends A {
constructor({ b = "bar", ...rest } = {}) {
super(rest);
console.log("new B " + b);
}
}
new B({a:2}); // prints 'new A 2', 'new B bar'
new B({a:4, b:5, c:6}); // throws 'Unexpected parameters'
In the above snippet parent classes don't see the params consumed by the descendants. If you have problems with that you can do it either as #Bergi or #loganfsmyth suggested. For example:
class A {
constructor(params = {}) {
const { a = "foo" } = params;
console.log("a=" + a);
}
}
class B extends A {
constructor(params = {}) {
const { b = "bar" } = params;
super(params);
console.log("b=" + b);
}
}
new B({a:2}); // prints a=2 b=bar
new B({b:5}); // prints a=foo b=5

A quick-win is to use the arguments object. It is an array containing all parameters passed to a function.
More information on the MDN.
In practice, you can access to the first parameter of your function thanks to arguments[0].
class Child extends Parent {
constructor({ parentOnlyArg = 'value',
visibleStyle = 'inline' } = {}) {
super(arguments[0]);
[...]
}
}

Related

How to create functions base on config in javascript class

I can defined readFoo in Foo class:
var myFormat = 'foo'
class Foo {
[ "read" + ((format) => format)(myFormat) ]() {
return 123;
}
}
is there any way how to define function base on config like:
var config = ['foo1', 'foo2']
class Foo {
config.map((name) => {
[ "read" + ((format) => format)(name) ]() {
return 123;
}
}
}
Will create functions readFoo1 and readFoo2.
You can iterate through the array and assign to the prototype afterwards:
var config = ['foo1', 'foo2']
class Foo {}
for (const name of config) {
Foo.prototype["read" + name] = function() {
return 123;
};
}
const f = new Foo();
console.log(f.readfoo1());

For ... in not yielding methods

Given the following:
export class MyClass {
public dataA = 0
private dataB = 123
public myMethod(): any {
return {
test: 'true'
}
}
constructor() {
for (const propOrMethod in this) {
console.log({propOrMethod})
}
}
}
const myInst = new MyClass()
I run this with ts-node index.ts and all i get is:
{ propOrMethod: 'dataA' }
{ propOrMethod: 'dataB' }
With no reference to myMethod. I would like to iterate over all the methods of my class, but they don't appear to exist
for..in iterates over all enumerable properties of the instance and up the prototype chain. But normal methods in a class are not enumerable:
class MyClass {
myMethod() {
return {
test: 'true'
};
}
}
console.log(Object.getOwnPropertyDescriptor(MyClass.prototype, 'myMethod').enumerable);
So it doesn't get iterated over.
If you want to iterate over non-enumerable properties as well, use Object.getOwnPropertyNames (which iterates over the object's own property names, so you'll need to do so recursively if you want all property names anywhere in the prototype chain):
const recurseLog = obj => {
for (const name of Object.getOwnPropertyNames(obj)) {
console.log(name);
}
const proto = Object.getPrototypeOf(obj);
if (proto !== Object.prototype) recurseLog(proto);
};
class MyClass {
dataA = 0;
dataB = 123;
constructor() {
recurseLog(this);
}
myMethod() {
return {
test: 'true'
};
}
}
const myInst = new MyClass();
You could also make the method enumerable:
class MyClass {
dataA = 0;
dataB = 123;
constructor() {
for (const propOrMethod in this) {
console.log({propOrMethod})
}
}
myMethod() {
return {
test: 'true'
};
}
}
Object.defineProperty(MyClass.prototype, 'myMethod', { enumerable: true, value: MyClass.prototype.myMethod });
const myInst = new MyClass();
Or assign the method after the class definition:
class MyClass {
dataA = 0;
dataB = 123;
constructor() {
for (const propOrMethod in this) {
console.log({propOrMethod})
}
}
}
MyClass.prototype.myMethod = () => ({ test: 'true' });
const myInst = new MyClass();
Or assign it to the instance in the constructor:
class MyClass {
dataA = 0;
dataB = 123;
constructor() {
this.myMethod = this.myMethod;
for (const propOrMethod in this) {
console.log({propOrMethod})
}
}
myMethod() {
return {
test: 'true'
};
}
}
const myInst = new MyClass();

Setting a variable equal to a return of function

I'm trying to set a variable equal to a return of a function but I don't understand how can i do.
In particular this is the code:
constructor() {
super();
this.manager = new BleManager()
this.state = {
info: "",
values: {}
}
this.deviceprefix = "FM_RAW";
this.devicesuffix_dx = "DX";
}
model_dx(model) {
return this.deviceprefix + model + this.devicesuffix_dx
}
if (device.name === "THERE i should use the return of model_dx") {
this.info(device.id)
this.manager.stopDeviceScan();
device.connect()
I should check device.name with the result of the model_dx function. How can I do?
Thank you
How about calling it? Create a instance of the object and call it:
// Assume the name is CustomObj
class CustomObj {
constructor() {
super();
this.manager = new BleManager()
this.state = {info: "", values: {}}
this.deviceprefix = "FM_RAW";
this.devicesuffix_dx = "DX";
}
model_dx(model) {
return this.deviceprefix + model + this.devicesuffix_dx
}
}
// I suppose this is outside of the object? Otherwise it would be out of scope anyways as you wrote your if in no function or whatsoever
CustomObj obj = new CustomObj(); //<-- Create instance
let alwaysdifferentParam = "model test";
if (device.name === obj.model_dx(alwaysdifferentParam )) { //<-- Call it
this.info(device.id)
this.manager.stopDeviceScan();
device.connect()
}
Try this:
if (device.name === this.model_dx('pass the desired value here')) {
this.info(device.id)
this.manager.stopDeviceScan();
device.connect()
}

Transforming an object with a javascript class

Iā€™m transforming data I receive from an API. The frontend requires some calculations to be displayed.
What is the proper way to handle the data transformation?
Should I be defining a property to the object being passed? If so, why
Is this a good use case to use setters and getters or would that be unnecessary?
const dogData = {
dog_name: "filo",
born_time: 1530983852,
coat_color: "brown"
};
class Dog {
constructor(data) {
//do I need to set this.dog to the data object, what's the benefit of doing so?
this.dog = data;
this.name = this.dog.dog_name;
// vs
this.name = data.dog_name;
//Should I use setters and getters?
this.color = this.dog.coat_color;
// vs
this._color = this.dog.coat_color;
this.age = this.calculateAge();
}
calculateAge() {
return Date.now().getTime() - this.dog.born_time;
}
//Is this a good case where I should using getters to access the properties or would that be superfluous?
//should I be setting the properties with setters in this case?
get color() {
return this._color;
}
}
const dog = new Dog(dogData)
Your don't need to make a copy of data into your class.
You can assign the class fields directly (using object destructuring to be more readable).
const data = {
dog_name: 'filo',
born_time: 1530983852,
coat_color: 'brown'
}
class Dog {
// Directly assign values
constructor({ dog_name, born_time, coat_color }) {
this.name = dog_name
this.bornAt = born_time
this.color = coat_color
}
// Getter for computed properties
get age() {
return Date.now() - this.bornAt
}
}
const dog = new Dog(data)
Getters are needed only for computed property (dynamic or formatted values).
Good exemple:
class Person {
constructor({ firstname, lastname }) {
this.firstname = firstname
this.lastname = lastname
}
get fullname() {
return `${this.firstname} ${this.lastname}`
}
}
class Dog {
constructor(data) {
const {
dog_name: name,
born_time: age,
coat_color: color
} = data;
Object.assign(this, {
name,
age,
color
});
}
}
const dogData = {
dog_name: "filo",
born_time: 1530983852,
coat_color: "brown"
};
const dog = new Dog(dogData);
console.log(dog.name);
Q:
Shall I nevertheless throw in a possible read only approach? ā€“ Peter Seliger
A:
It wouldn't hurt. I appreciate the different approaches. ā€“ Matthew Moran
... here we go ...
// module start ... e.g. file: "Dog.js"
// locally scoped helper function
function calculateAge(dateOfBirth) {
return (Date.now() - dateOfBirth);
}
/*export default */class Dog {
constructor(initialValue) {
Object.defineProperties(this, {
valueOf: { // just in order to hint what `initialValue` might still be good for.
value: function () {
return Object.assign({}, initialValue);
}
},
name: {
value: initialValue.dog_name,
enumerable: true
},
color: {
value: initialValue.coat_color,
enumerable: true
},
age: {
get() {
return calculateAge(initialValue.born_time);
},
enumerable: true,
}
});
}
}
// module end.
// test
const dogData = {
dog_name: "filo",
born_time: 1530983852,
coat_color: "brown"
};
const dog = new Dog(dogData);
console.log('Object.keys(dog) : ', Object.keys(dog));
console.log('dog.valueOf() : ', dog.valueOf());
console.log('dog.age : ', dog.age);
console.log('dog.name : ', dog.name);
console.log('dog.color : ', dog.color);
console.log('(dog.age = 298146912) : ', (dog.age = 298146912) && dog.age);
console.log('(dog.name = "spot") : ', (dog.name = "spot") && dog.name);
console.log('(dog.color = "black") : ', (dog.color = "black") && dog.color);
.as-console-wrapper { max-height: 100%!important; top: 0; }

Is it possible to Proxy an extended class in javascript

I'm trying to Proxy an inheritance structure from within a node module and allow the client to instantiate a new Class A. Currently when trying to access class B's parent methods I get a.parentMethod is not a function
handler.js ->
module.exports = {
get(target, key, receiver) {
return target.getAttribute(key)
},
set(target, key, value, receiver) {
return target.setAttribute(key, value)
}
}
A.js ->
const handler = require('handler')
class B {
constructor(data) {
this.data = data
}
parentMethod() {
... do stuff
}
}
class A extends B {
constructor(data){
super(data)
}
}
module.exports = function(data) {
return new Proxy(new A(data), handler)
}
////
const A = require('A')
var a = new A
a.parentMethod()
Where am I going wrong with this structure? I'm new to Proxy!
Thanks
EDIT -
Further context:
I'm trying to keep sets of properties in sync based on a valueSchema I have defined. When I set Artwork.title I need Artwork['Artwork Title'] to be updated with the same value. Likewise when I retrieve Artwork.title I get the value of Artwork['Artwork Title']. Hopefully this helps a bit. I'm stuck at the above error so I can't be sure what I've written actually works yet! I'm trying to debug why the function can't be found first...
class Instance {
constructor(data) {
this._valueAttributes = {}
}
setAttribute(key, value) {
if (this._isValueAttribute(key)) {
return this._getSetValueAttribute(key, value)
}
throw Error('Cannot set invalid property '+key+' on instance.')
}
getAttribute(key) {
if (this._isValueAttribute(key)) {
return this._getSetValueAttribute(key)
}
}
_getSetValueAttribute(key, value) {
let schemaKey = this._getSchemaKey(key)
if (_.isFunction(schemaKey)) {
return alias(data)
}
if (value === undefined) {
return this._valueAttributes[schemaKey]
}
return this._valueAttributes[schemaKey] = value
}
_isValueAttribute(key) {
return _.keys(this._valueSchema).indexOf(key) === -1
}
}
class Artwork extends Instance {
constructor() {
this._valueSchema = {
medium: 'Artwork Medium',
title: 'Artwork Title'
}
}
}
///
a = new Artwork
a.title = 'thing'
a['Artwork Medium'] = 'medium';
I need
a.title == a['Artwork Title']
a['Artwork Medium'] == a.medium
It's very likely I've royally screwed it all up. I've assumed that I can access __valueSchema on the child from the parent. Is this not possible?

Categories