I am playing with ES6 classes and to better manage an array property of the class, I replaced the array with an object and added all the array-related functions (get, add, remove, etc) along with an array sub-property:
class MyClass {
constructor () {
this.date_created = new Date()
}
posts = {
items: [],
get: () => {
return this.posts.items
},
add: (value) => this.posts.items.unshift(value),
remove: (index) => this.posts.items.splice(index, 1)
}
}
So it got me thinking: is there a way to setup that posts object to return the items array by default? i.e. through: MyClass.posts
I thought I could trick it with the get() but didn't work.
If you want to keep the actual array hidden and untouchable except through the methods, it has to be declared in the constructor. Any function that alters it has to be declared in the constructor as well. If that item is to be publicly accessible, it has to be attached to this.
class Post extends Array
{
add(val)
{
this.unshift(val);
}
remove()
{
this.shift();
}
}
class MyClass
{
constructor()
{
this.date_created = new Date()
this.post = new Post();
}
}
let x = new MyClass();
console.log(x.post);
x.post.add(2);
console.log(x.post);
class MyClass {
constructor() {
this.post = [];
}
get post() {
return [1,2,3]
}
set post(val) {
}
}
let someClass = new MyClass();
console.log(someClass.post)
I believe the correct syntax is something as above. your getter and setter is pointing to post, where post is only a variable/key of MyClass, hence your getter and setter should be at MyClass level
Related
I tried to write a mixin function based on the ideas described here for inheritance. Method mixins are pretty easy, but I am struggling with constructor mixins.
I try to write a function, which takes a class and returns a new class, with an extended constructor.
This is my example:
function extend_constructor(original, extension) {
let extended = function (...args) {
console.log("extended constructor")
original.prototype.constructor.apply(this, args)
extension.apply(this, args)
}
Object.defineProperty(extended, 'name', Object.getOwnPropertyDescriptor(original, 'name'))
extended.prototype = Object.create(original.prototype)
Object.defineProperty(extended.prototype, 'constructor', {
value: extended,
writeable: true,
configurable: true})
return extended
}
function extension(id) {
console.log(`Extension: ${id}`)
}
I works with function classes.
function Basefunc(id) {
console.log(`Basefunc: ${id}`)
}
new Basefunc(1)
const BasefuncEx = extend_constructor(Basefunc, extension)
new BasefuncEx(2)
But fails with ES6 classes:
class Baseclass {
constructor(id) {
console.log(`Baseclass: ${id}`)
}
}
new Baseclass(3)
const BaseclassEx = extend_constructor(Baseclass, extension)
new BaseclassEx(4)
The error is:
TypeError: Class constructor Baseclass cannot be invoked without 'new'
But I think using new is not what I want, because new will create an instance of the original class and will apply the original constructor to it. I want to apply the original constructor of the original class to the instance of the extended class. And I am wondering how to do this with new.
I can create custom decorator using reflect-metadata and it work fine.
Problem is, that I don`t know how to get all instance decorators.
import 'reflect-metadata';
console.clear();
function readTypes() {
const decorator: MethodDecorator = (target, propertyKey, description) => {
const args = Reflect.getMetadata(
'design:paramtypes',
target,
propertyKey
).map(c => c.name);
const ret = Reflect.getMetadata('design:returntype', target, propertyKey);
console.log(`Arguments type: ${args.join(', ')}.`);
console.log(`Return type: ${ret.name}.`);
};
return decorator;
}
class Foo {}
class Bar {
#readTypes()
public fn(a: number, b: string, c: Foo): boolean {
return true;
}
}
const barInstance = new Bar();
I would like to get all functions with decorator #readTypes from barInstance. How can I do it?
See working example:
https://stackblitz.com/edit/decorators-metadata-example-nakg4c
First of all, you aren't writing any metadata, only reading it. If you want to lookup which properties were decorated, then you have to write metadata to those properties.
For simplicity's sake, let's simplify the decorator to:
// It's a best practice to use symbol as metadata keys.
const isReadTypesProp = Symbol('isReadTypesProp')
function readTypes() {
const decorator: MethodDecorator = (target, propertyKey, description) => {
Reflect.defineMetadata(isReadTypesProp, true, target, propertyKey);
};
return decorator;
}
Now when the decorator is used, it's executed when the class is parsed, not when instances are created. This means that target in the decorator function is actually the prototype of the class constructor function.
In other words target is the same object as
Bar.prototype or barInstance.constructor.prototype or barInstance.__proto__.
Knowing that we can loop over all the property names in the prototype object and look up that metadata we set earlier:
function getReadTypesPropsFromInstance(object: {
constructor: {
prototype: unknown;
};
}) {
const target = object.constructor.prototype;
const keys = Object.getOwnPropertyNames(target);
return keys.filter(key => Reflect.getMetadata(isReadTypesProp, target, key));
}
Which now returns the names of the decorated properties:
const barInstance = new Bar();
console.log(getReadTypesPropsFromInstance(barInstance)); // ["fn"]
Working example
I would like to convert a function that return multiple functions, into a class with a constructor that should be called only with the new keyword.
I tried this :
const toast = () => {
return ({
getToast: () => {
return 'toast'
},
setToast: () => {
return 'wtf'
}
})
}
class t {
constructor() {}
}
const t1 = t.bind(toast())
const tt = new t1()
console.log(tt.getToast)
But it print undefined.
I also tried Object.assign(t, toast()), or doing simply this = toast() in the constructor but it doesn't work.
I do not know what are you trying to do. May be you are looking for this.
const toast = () => {
return ({
getToast: () => {
return 'toast'
},
setToast: () => {
return 'wtf'
}
})
}
class t {
constructor(fn) {
return fn;
}
}
const tt = new t(toast())
console.log(tt.getToast())
console.log(tt.setToast())
For your exact scenario, that is if the function returns an object with only functions and no non-function properties, one way to do this is simply using prototype inheritance:
function t () {}; // though by convention a class should REALLY be uppercase
t.prototype = toast();
Now you can do:
let bread = new t();
let bagel = new t();
bread.getToast();
bagel.getToast();
You can also use a design pattern called parasitic inheritance but this is less memory efficient because just like the toast() function it creates a copy of the functions for each object:
class t {
constructor () {
let tmp = toast();
// Copy methods to our instance:
for (let prop in tmp) {
this[prop] = tmp[prop];
}
}
}
Or with Object.assign() it would simply be:
class t {
constructor () {
Object.assign(this, toast());
}
}
However, as mentioned, the prototype inheritance is the better mechanism for this use-case.
Am I missing something here? I have a Class which I use to instantiate and create objects. These objects have prototype methods on them.
let item = {
name: 'Television',
brand: 'Sony'
}
class Product {
constructor (item) {
Object.assign(this, item)
}
analyzeBrand () {
// do something here
}
}
let product1 = new Product(item);
And then in my store, I add this product to an array
commit('addProduct', product1)
mutations: {
addProduct (item) {
state.products.unshift(item);
}
}
But then when I inspect the instance of my product later in a Vue component, the prototype method analyzeBrand has been removed. What gives?
Good day,
I dont know if am can explain this well for you to help but i will like to use a an ES6 class to create an object that can be called like this.
var = varaibles
obj = objects
obj.var
obj.var.method
obj.var.var.method
obj.method.var
and so on.
I can only do one step
obj.var && obj.method
i will kind appreciate if one can help me here thanks
this is what i have done
class Table extends someClass {
constructor() {
super();
this.column = {
sort: () => {
console.log("firing");
},
resize: () => {
console.log("firing");
}
};
this.cells = {
edit: () => {
console.log("firing");
}
};
}
myMethods() {
//BLAH
}
}
From what I understood, here is my solution.
If I return a object full of methods, I can use that object as I like.
class someClass {
// this is a parent method
Parent() {
console.log(`From a Parent`)
}
// a getter that returns an object
get parentWithChild() {
return {
child() {
console.log(`From a Child`)
}
}
}
// a function that returns an object
Methods() {
return {
child() {
console.log(`From a Child`)
}
}
}
}
const cool = new someClass();
cool.Parent(); // From a Parent
cool.parentWithChild.child(); // From a Child
cool.Methods().child(); // From a Child
You can use similar pattern on the extended class too.