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();
Related
I'm trying to implement a builder pattern in javascript. I have a class Director that can build different animals.
The animal is created in a class called AnimalBuilder. The idea is to get a collection of methods to instantiate many different keys in an object which will be very different based on the animal.
It almost works. The constructor of the AnimalBuilder class is properly updated, but I can't access this simple object so far. All I receive is a constructor object.
class AnimalBuilder {
constructor(name) {
this.name = name;
}
setSpecy(specy) {
this.specy = specy;
return this;
}
setIsComestible(isComestible) {
this.isComestible = isComestible;
return this;
}
create() {
return this;
}
}
class Director {
buildCat(builder) {
return builder
.setSpecy("cat")
.setIsComestible(false)
.create();
}
buildCow(builder) {
return builder
.setSpecy("cow")
.setIsComestible(true)
.create();
}
}
const director = new Director();
const animalBuilder = new AnimalBuilder("Jenny");
const cow = director.buildCow(animalBuilder);
console.log(cow);
// output: AnimalBuilder {
// name: "Jenny",
// specy: "cow",
// isComestible: true
// }
How to only receive:
{
name: "Jenny",
specy: "cow",
isComestible: true
}
Thanks!
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.
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.
This question already has answers here:
Private properties in JavaScript ES6 classes
(41 answers)
Closed 4 years ago.
I am trying to have a private property inside my class. I have a class called Person and an instance of that class: person declared with let person = new Person('name1').
I would like to save the name in person's properties. I could simply do:
class Person {
constructor(name) {
this.name = name;
}
}
But I also want to perform some actions when changing this value, so I use a Setter:
class Person {
set name() {
// some actions
}
constructor(name) { }
}
But then how do I save the name? I would have to have another property for example _name that would be used to save the actual value
class Person {
set name(newName) {
// some actions
return this._name;
}
set name(newName) {
this._name = name;
// some actions
}
constructor(name) {
this._name = name;
}
}
The problem is that _name can be accessed outside with person._name.
Is there a way to make it not accessible from outside?
I took inspiration from this answer (which doesn't use a getter and setter) and tried to enclose _name when defining the getter and setter. The following code doesn't work:
class Person {
constructor(name) {
var _name = name;
Object.defineProperties(this, {
"name": {
"get": () => { return _name; },
"set": () => { _name = name; }
}
});
}
}
"set": () => { _name = name; }
You just have a small mistake here. Should be:
"set": (newName) => { _name = newName; }
I am learning JS and have came across an interesting article about object composition pattern in JS. What I am wondering in this code:
let Magic = (superclass) => class extends superclass {
shout() {
if (super.shout) super.shout();
console.log('Power and wisdom.');
}
};
let Fighting = (superclass) => class extends superclass {
shout() {
if (super.shout) super.shout();
console.log('Strength an courage.');
}
};
class Creature {
constructor(name) {
this.name = name;
}
shout() {
console.log(`I'm ${this.name}! Oorah!!`);
}
};
class DwarfWizard extends Fighting(Magic(Creature)) {
courseObjects(object = {}) {
object.curse = true;
return object;
}
}
new DwarfWizard('Thordalf').shout();
// "I'm Thordalf! Oorah!! Power and wisdom. Strength an courage."
What is the purpose of the function courseObjects in the DwarfWizard class?
courseObjects(object = {}) {
object.curse = true;
return object;
}
I still get the same result even when I comment out the function, so I am wondering what is it's purpose?