JavaScript OOP Course 5: Composition (Mixins): Difference between revisions
m Стадий: 6 [Фаза:Утвърждаване, Статус:Утвърден]; Категория:JavaScript |
m Text replacement - "mlw-continue" to "code-continue" |
||
Line 8: | Line 8: | ||
'''With the Composition technique we can compose few objects together to create a new object.''' The Composition technique has a great flexibility. Here is how it works. | '''With the Composition technique we can compose few objects together to create a new object.''' The Composition technique has a great flexibility. Here is how it works. | ||
1. Let's define few objects that contains the methods (features) we need to mix-in at our application objects. <syntaxhighlight lang="javascript" class=" | 1. Let's define few objects that contains the methods (features) we need to mix-in at our application objects. <syntaxhighlight lang="javascript" class="code-continue"> | ||
const canEat = { | const canEat = { | ||
eat: function() { | eat: function() { | ||
Line 24: | Line 24: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
2. Now we can use these objects together to create a person that can <code>.eat()</code> and <code>.walk()</code>. In ES6 we can use the <code>Object.assign()</code> method. As the first argument to that method we must pass an object, where the members (properties and methods) of the rest objects (passed as the rest arguments) will be copied. | 2. Now we can use these objects together to create a person that can <code>.eat()</code> and <code>.walk()</code>. In ES6 we can use the <code>Object.assign()</code> method. As the first argument to that method we must pass an object, where the members (properties and methods) of the rest objects (passed as the rest arguments) will be copied. | ||
<syntaxhighlight lang="javascript" class=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
const person = Object.assign({hunger: null}, canEat, canWalk); | const person = Object.assign({hunger: null}, canEat, canWalk); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="javascript" class=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
person; | person; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="shell-session" class=" | <syntaxhighlight lang="shell-session" class="code-continue"> | ||
{hunger: null, eat: ƒ, walk: ƒ} | {hunger: null, eat: ƒ, walk: ƒ} | ||
hunger: null | hunger: null | ||
Line 37: | Line 37: | ||
[[Prototype]]: Object | [[Prototype]]: Object | ||
</syntaxhighlight> | </syntaxhighlight> | ||
3. Create a constructor function by using this technique. Here we need to pass <code>this</code> of the newly created objects to the <code>Object.assign()</code> method.<syntaxhighlight lang="javascript" class=" | 3. Create a constructor function by using this technique. Here we need to pass <code>this</code> of the newly created objects to the <code>Object.assign()</code> method.<syntaxhighlight lang="javascript" class="code-continue"> | ||
function Person() { | function Person() { | ||
this.hunger = null; | this.hunger = null; | ||
Line 43: | Line 43: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="javascript" class=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
const person1 = new Person(); | const person1 = new Person(); | ||
person1; | person1; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="shell-session" class=" | <syntaxhighlight lang="shell-session" class="code-continue"> | ||
Person {hunger: null, eat: ƒ, walk: ƒ} | Person {hunger: null, eat: ƒ, walk: ƒ} | ||
hunger: null | hunger: null | ||
Line 56: | Line 56: | ||
Or we can assign the methods <code>.eat()</code> and <code>.walk()</code> at the Prototype level of our constructor function, which is better because will reduce the resources used by our program - the methods will be available at the prototype level and not be replicated at each single instance of the object. | Or we can assign the methods <code>.eat()</code> and <code>.walk()</code> at the Prototype level of our constructor function, which is better because will reduce the resources used by our program - the methods will be available at the prototype level and not be replicated at each single instance of the object. | ||
<syntaxhighlight lang="javascript" class=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
function Person() { | function Person() { | ||
this.hunger = null; | this.hunger = null; | ||
Line 62: | Line 62: | ||
Object.assign(Person.prototype, canEat, canWalk); | Object.assign(Person.prototype, canEat, canWalk); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="javascript" class=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
const person2 = new Person(); | const person2 = new Person(); | ||
person2; | person2; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="shell-session" class=" | <syntaxhighlight lang="shell-session" class="code-continue"> | ||
Person {hunger: null} | Person {hunger: null} | ||
hunger: null | hunger: null | ||
Line 75: | Line 75: | ||
[[Prototype]]: Object | [[Prototype]]: Object | ||
</syntaxhighlight> | </syntaxhighlight> | ||
4. Let's imagine tomorrow we need to add two new objects to our application - Goldfish and Dog, and both of them should have capability to swim.<syntaxhighlight lang="javascript" class=" | 4. Let's imagine tomorrow we need to add two new objects to our application - Goldfish and Dog, and both of them should have capability to swim.<syntaxhighlight lang="javascript" class="code-continue"> | ||
const canSwim = { | const canSwim = { | ||
swim: function() { | swim: function() { | ||
Line 85: | Line 85: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Then we can define new constructors. | Then we can define new constructors. | ||
<syntaxhighlight lang="javascript" class=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
function Dog() { | function Dog() { | ||
this.hunger = null; | this.hunger = null; | ||
Line 91: | Line 91: | ||
Object.assign(Dog.prototype, canEat, canWalk, canSwim); | Object.assign(Dog.prototype, canEat, canWalk, canSwim); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="javascript" class=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
const dog1 = new Dog(); | const dog1 = new Dog(); | ||
dog1; | dog1; | ||
Line 106: | Line 106: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="javascript" class=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
function Goldfish() { | function Goldfish() { | ||
this.hunger = null; | this.hunger = null; | ||
Line 112: | Line 112: | ||
Object.assign(Goldfish.prototype, canEat, canSwim); | Object.assign(Goldfish.prototype, canEat, canSwim); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="javascript" class=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
const goldfish1 = new Goldfish(); | const goldfish1 = new Goldfish(); | ||
goldfish1; | goldfish1; | ||
Line 126: | Line 126: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
5. In order to make our code more readable we can extract the logic related to <code>Object.assign()</code> in a function that use the ''rest'' and ''spread'' operator and is called <code>mixin(target, ...sources)</code>.<syntaxhighlight lang="javascript" class=" | 5. In order to make our code more readable we can extract the logic related to <code>Object.assign()</code> in a function that use the ''rest'' and ''spread'' operator and is called <code>mixin(target, ...sources)</code>.<syntaxhighlight lang="javascript" class="code-continue"> | ||
function mixin(targetConstructor, ...sources) { // here '...' is the rest operator | function mixin(targetConstructor, ...sources) { // here '...' is the rest operator | ||
Object.assign(targetConstructor.prototype, ...sources); // here '...' is the spread operator | Object.assign(targetConstructor.prototype, ...sources); // here '...' is the spread operator |
Latest revision as of 07:29, 26 September 2022
References
- Code with Mosh: The Ultimate JavaScript Mastery Series – Part 2
- W3School: JavaScript Tutorial
Composition Technique
With the Composition technique we can compose few objects together to create a new object. The Composition technique has a great flexibility. Here is how it works.
1. Let's define few objects that contains the methods (features) we need to mix-in at our application objects.
const canEat = {
eat: function() {
this.hunger--;
console.log('eating');
}
};
const canWalk = {
walk: function() {
this.hunger++;
console.log('walking');
}
};
2. Now we can use these objects together to create a person that can .eat()
and .walk()
. In ES6 we can use the Object.assign()
method. As the first argument to that method we must pass an object, where the members (properties and methods) of the rest objects (passed as the rest arguments) will be copied.
const person = Object.assign({hunger: null}, canEat, canWalk);
person;
{hunger: null, eat: ƒ, walk: ƒ}
hunger: null
eat: ƒ ()
walk: ƒ ()
[[Prototype]]: Object
3. Create a constructor function by using this technique. Here we need to pass this
of the newly created objects to the Object.assign()
method.
function Person() {
this.hunger = null;
Object.assign(this, canEat, canWalk);
}
const person1 = new Person();
person1;
Person {hunger: null, eat: ƒ, walk: ƒ}
hunger: null
eat: ƒ ()
walk: ƒ ()
[[Prototype]]: Object
Or we can assign the methods .eat()
and .walk()
at the Prototype level of our constructor function, which is better because will reduce the resources used by our program – the methods will be available at the prototype level and not be replicated at each single instance of the object.
function Person() {
this.hunger = null;
}
Object.assign(Person.prototype, canEat, canWalk);
const person2 = new Person();
person2;
Person {hunger: null}
hunger: null
[[Prototype]]: Object
eat: ƒ ()
walk: ƒ ()
constructor: ƒ Person()
[[Prototype]]: Object
4. Let's imagine tomorrow we need to add two new objects to our application – Goldfish and Dog, and both of them should have capability to swim.
const canSwim = {
swim: function() {
this.hunger++;
console.log('swi');
}
};
Then we can define new constructors.
function Dog() {
this.hunger = null;
}
Object.assign(Dog.prototype, canEat, canWalk, canSwim);
const dog1 = new Dog();
dog1;
Dog {hunger: null}
hunger: null
[[Prototype]]: Object
eat: ƒ ()
swim: ƒ ()
walk: ƒ ()
constructor: ƒ Dog()
[[Prototype]]: Object
function Goldfish() {
this.hunger = null;
}
Object.assign(Goldfish.prototype, canEat, canSwim);
const goldfish1 = new Goldfish();
goldfish1;
Goldfish {hunger: null}
hunger: null
[[Prototype]]: Object
eat: ƒ ()
swim: ƒ ()
constructor: ƒ Goldfish()
[[Prototype]]: Object
5. In order to make our code more readable we can extract the logic related to Object.assign()
in a function that use the rest and spread operator and is called mixin(target, …sources)
.
function mixin(targetConstructor, ...sources) { // here '...' is the rest operator
Object.assign(targetConstructor.prototype, ...sources); // here '...' is the spread operator
}
Now we can use the mixin(target, …sources)
function to simplify our code in the following way.
function Dog() { this.hunger = null; }
mixin(Dog, canEat, canWalk, canSwim);
function Goldfish() { this.hunger = null; }
mixin(Goldfish, canEat, canSwim);