Function returning objects with getters and methods - javascript

I was refactoring my code and decided to replace ES6-classes with this-independent factory functions. So before the changes I had something like this:
class Person {
constructor(name) {
this.name = name;
}
get myName() {
return `My name if ${this.name}`;
}
say() {
console.log(this.myName);
}
}
const john = new Person('John');
john.say();
Then it was changed to:
function Person(name) {
const obj = {
name,
get myName() {
return `My name if ${obj.name}`;
},
say() {
console.log(obj.myName);
},
};
return obj;
}
const john = Person('John');
john.say();
It worked just the same, but when I used it in my Vue app, dealing with thousands of such objects, its performance dropped dramatically. I tried to split data, getters and methods into different objects and then to return their combination { ...data, ...getters, ...methods }, but nothing changed — I saw that classes were much more performant.
After some googling I found the possibility to add getters and methods via defineProperty:
function Person(name) {
const obj = {
name,
};
Object.defineProperty(obj, 'myName', {
get() {
return `My name if ${obj.name}`;
}
});
Object.defineProperty(obj, 'say', {
value() {
console.log(obj.myName);
}
})
return obj;
}
const john = Person('John');
john.say();
This solution worked, the application regained its performance, but I don't understand why. The only difference is probably that now the added properties aren't enumerable. But how could that make a problem?
So I would be very grateful for an explanation and probably a better solution.

Related

can I add non static method to external class using prototypes?

Can I add using prototypes function for a class instance?
so I'll be able to use this or __proto__ keyword inside my method, for example:
class PersonClass {
name: string;
constructor(name: string) {
this.name = name;
}
sayHello() {
console.log(`hello, my name is ${this.name} and I'm a ${this.type}`);
}
}
PersonClass.prototype.type = "human";
PersonClass.prototype.PrintType = () => {
console.log(`I'm a ${PersonClass.prototype.type}`);
};
const aria = new PersonClass("Ned Stark");
aria.sayHello();
aria.PrintType();
this code works of course, but I wish to add something like
PersonClass.prototype.SayHello2 = () => {
console.log(this.name, caller.__proto__.name);
};
which of course fails.
is it even possible?
Your SayHello2 should be a non-arrow function to access the properties you are looking for:
PersonClass.prototype.SayHello2 = function () {
console.log(this.name, this.type);
};
which produces:
"Ned Stark", "human"
Don't forget you also have access to the constructor property of an instance as well, allowing you to access everything associate with your classes.
This should works:
PersonClass.prototype.SayHello2 = function() {
console.log(this.name);
};
aria.SayHello2(); // "Ned Stark"

Composition Pattern in Javascript with Classes but without Mixins?

Is the following a valid strategy for implementing the composition pattern in Javascript? I want to use classes instead of constructor functions or plain objects, and I know that Mixins are not best practice. One concern is that in this approach, the methods added to Person objects are not attached to the prototype and therefore each require memory allocation. Thanks!
class Person {
name;
constructor(name) {
this.name = name;
}
}
function fly() {
return {
fly() {
console.log(`${this.name} can fly!`);
},
};
}
function swim() {
return {
swim() {
console.log(`${this.name} can swim!`);
},
};
}
function makeFlyingPerson(name) {
return Object.assign(new Person(name), fly());
}
function makeSwimmingPerson(name) {
return Object.assign(new Person(name), swim());
}
...the methods added to Person objects are not attached to the prototype and therefore each require memory allocation
True, but it's a trivial amount, the cost of one property per method per object (to hold the function reference for the method). Properties aren't nothing, but they aren't large. For the avoidance of doubt: The function object is reused by all instances, not copied.
There's no reason for fly and swim to be functions, though (at least, none that's apparent from the question), just use the objects directly:
class Person {
name;
constructor(name) {
this.name = name;
}
}
const flyMethods = {
fly() {
console.log(`${this.name} can fly!`);
},
};
const swimMethods = {
swim() {
console.log(`${this.name} can swim!`);
},
};
function makeFlyingPerson(name) {
return Object.assign(new Person(name), flyMethods);
}
function makeSwimmingPerson(name) {
return Object.assign(new Person(name), swimMethods);
}
Note that this is still using mixins, though (both your original and the above).
Unless you're going to reuse fly/flyMethods and swim/swimMethods with other classes than Person, though, using extends would seem simpler and would give you prototypical method reuse:
class FlyingPerson extends Person {
fly() {
// ...
}
}
If you are reusing fly/flyMethods, etc., with multiple classes, another option is to have factory-building functions that create a prototype from the various sets of methods and then reuse it:
class Person {
name;
constructor(name) {
this.name = name;
}
}
const flyMethods = {
fly() {
console.log(`${this.name} can fly!`);
},
};
const swimMethods = {
swim() {
console.log(`${this.name} can swim!`);
},
};
function extendWith(cls, name, ...mixins) {
// We use the wrapper object so that the class constructor's name is assigned from `name`
const obj = {
[name]: class extends cls {
}
};
Object.assign(obj[name].prototype, ...mixins);
return obj[name];
}
const FlyingPerson = extendWith(Person, "FlyingPerson", flyMethods);
const SwimmingPerson = extendWith(Person, "SwimmingPerson", swimMethods);
const FlyingSwimmingPerson = extendWith(Person, "FlyingSwimmingPerson", flyMethods, swimMethods);
const joe = new FlyingSwimmingPerson("Joe");
joe.fly();
joe.swim();
class Animal {
name;
type;
constructor(name, type) {
this.name = name;
this.type = type;
}
}
const FlyingSwimmingAnimal = extendWith(Animal, "FlyingSwimmingAnimal", flyMethods, swimMethods);
console.log(FlyingSwimmingAnimal.name); // FlyingSwimmingAnimal
const splippery = new FlyingSwimmingAnimal("Slippery");
splippery.fly();
splippery.swim();

How to recognize or identify an object made by a factory

What would be the best way to reproduce the instanceof functionality for an object made by a factory ?
Example:
class Cat {
constructor(name) {
this.name = name;
}
}
const tom = new Cat('tom');
tom instanceof Cat // true
VS
const makeCat = (name) => ({
name
})
const garfield = makeCat('garfield');
// ???
What would be the best way to identify the garfield object as a "cat" since it was made by makeCat ?
As it feels while discussing in the comments like there is no "best practice" solution for this (without using a class at least, which I don't want to), this would probably be my implementation for this purpose:
const cat = Symbol('cat');
export const makeCat = (name) => {
return {
[cat]: true,
name
}
};
export const isCat = (obj) => obj[cat];
import { makeCat, isCat } from './CatFactory.js';
const garfield = makeCat('garfield');
const tom = { cat: true, name: 'tom' };
isCat(garfield); // true
isCat(tom); // false
It's the less hacky way I found to recreate any sort of 'certainty' while trying to identify an object made by my factory and without making it possible to create a cat by mistake or even duck-typing and without polluting a console.log of the object created for example.

How to convert a function into a class?

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.

Function as unlabeled prop in JavaScript. What is this called, and how does it work?

I saw this code today, which does something I've never seen before. It has an object which itself has an unlabeled property that is a function.
emails = {
type: EmailType,
args: { id: { type: GraphQLID } },
resolve(parentValue, args) {
const query = `SELECT * FROM "emails" WHERE id=${args.id}`;
return db.conn.one(query)
.then(data => {
return data;
})
.catch(err => {
return 'The error is', err;
});
}
}
}
I'd like to know more about this, but I have no idea what the proper keyterm for this is, and searching "function as property js" only yields really obvious stuff (ie {someProp: () => 42}).
I'm certain that both:
A. If I knew the right key term, it would be really easy to learn more and
B. The only way to make this keyterm easier to find is to have something someone would actually search lead to it. To that end, I'll include some extra SEO:
object has function but not at prop
function inlined in object
function in object
object has a function but it's not a prop
no propname for function
Anyways:
What is this called, and where can I find more information on it?
EDIT: Got links to docs. One thing to denote is the differences between
// these are the same, I think
const eg1 = { someFn() {} }
const eg2 = { someFn: function() {} }
// this is different in scope... I think
const someFn = () => {};
const eg3 = { someFn };
It is a Shorthand method name.
{ method() { /*...*/ } }
is equal to:
{ method: function() { /*...*/ } }

Categories

Resources