JavaScript OOP Course 9: Exercises: Difference between revisions
m Стадий: 6 [Фаза:Утвърждаване, Статус:Утвърден]; Категория:JavaScript |
m Text replacement - "mlw-continue" to "code-continue" |
||
Line 10: | Line 10: | ||
* Properties: <code>duration</code> | * Properties: <code>duration</code> | ||
* Method: <code>reset()</code>, <code>start()</code>, <code>stop()</code>. | * Method: <code>reset()</code>, <code>start()</code>, <code>stop()</code>. | ||
<syntaxhighlight lang="javascript" class=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
function Stopwatch() { | function Stopwatch() { | ||
let startTime = null; let endTime = null; let running = false; let duration = 0; | let startTime = null; let endTime = null; let running = false; let duration = 0; | ||
Line 37: | Line 37: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="javascript" class=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
const sw = new Stopwatch(); | const sw = new Stopwatch(); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="javascript" class=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
sw.start(); | sw.start(); | ||
sw.stop(); | sw.stop(); | ||
sw.duration; | sw.duration; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="shell-session" class=" | <syntaxhighlight lang="shell-session" class="code-continue"> | ||
8.56 | 8.56 | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="javascript" class=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
sw.reset(); | sw.reset(); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Stopwatch (move all methods at prototype level) == | == Stopwatch (move all methods at prototype level) == | ||
In order to do that we need to give public access to the private properties (defined by the <code>let</code> keyword) as it is done below, but note this breaks the abstraction principle, for example: In order to set the <code>duration</code> value from the prototype level we need to create setter that private variable, but in the same time this setter function is accessible from outside... This example is designed to show that sometimes it is a bad practice to move some members at the prototype level - '''Premature optimization is the root of all evils'''.<syntaxhighlight lang="javascript" class=" | In order to do that we need to give public access to the private properties (defined by the <code>let</code> keyword) as it is done below, but note this breaks the abstraction principle, for example: In order to set the <code>duration</code> value from the prototype level we need to create setter that private variable, but in the same time this setter function is accessible from outside... This example is designed to show that sometimes it is a bad practice to move some members at the prototype level - '''Premature optimization is the root of all evils'''.<syntaxhighlight lang="javascript" class="code-continue"> | ||
function Stopwatch() { | function Stopwatch() { | ||
let duration = 0; let startTime = null; let endTime = null; let running = false; | let duration = 0; let startTime = null; let endTime = null; let running = false; | ||
Line 117: | Line 117: | ||
* Also should Prototypically Inherit the two methods - <code>click()</code> and <code>focus()</code> - from the <code>HtmlElement()</code> constructor. | * Also should Prototypically Inherit the two methods - <code>click()</code> and <code>focus()</code> - from the <code>HtmlElement()</code> constructor. | ||
'''1.''' Let's create and test <code>HtmlElement()</code>.<syntaxhighlight lang="javascript" class=" | '''1.''' Let's create and test <code>HtmlElement()</code>.<syntaxhighlight lang="javascript" class="code-continue"> | ||
function HtmlElement() { | function HtmlElement() { | ||
this.click = function() { | this.click = function() { | ||
Line 128: | Line 128: | ||
}; | }; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="javascript" class=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
const e = new HtmlElement(); | const e = new HtmlElement(); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="javascript" class=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
console.log(e); | console.log(e); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="shell-session" class=" | <syntaxhighlight lang="shell-session" class="code-continue"> | ||
HtmlElement {click: ƒ} | HtmlElement {click: ƒ} | ||
click: ƒ () | click: ƒ () | ||
Line 141: | Line 141: | ||
constructor: ƒ HtmlElement() | constructor: ƒ HtmlElement() | ||
[[Prototype]]: Object | [[Prototype]]: Object | ||
</syntaxhighlight><syntaxhighlight lang="javascript" class=" | </syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue"> | ||
e.click(); | e.click(); | ||
e.focus(); | e.focus(); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="shell-session" class=" | <syntaxhighlight lang="shell-session" class="code-continue"> | ||
clicked | clicked | ||
focused | focused | ||
</syntaxhighlight>'''2.''' Let's create and test the basic implementation of the <code>HtmlSelectElement()</code> constructor.<syntaxhighlight lang="javascript" class=" | </syntaxhighlight>'''2.''' Let's create and test the basic implementation of the <code>HtmlSelectElement()</code> constructor.<syntaxhighlight lang="javascript" class="code-continue"> | ||
function HtmlSelectElement(...args) { | function HtmlSelectElement(...args) { | ||
if (Array.isArray(args[0])) this.items = args[0]; | if (Array.isArray(args[0])) this.items = args[0]; | ||
Line 164: | Line 164: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="javascript" class=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
const s1 = new HtmlSelectElement([1, 2, 3]); | const s1 = new HtmlSelectElement([1, 2, 3]); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="javascript" class=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
console.log(s1); | console.log(s1); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="shell-session" class=" | <syntaxhighlight lang="shell-session" class="code-continue"> | ||
HtmlSelectElement {items: Array(3), addItem: ƒ, removeItem: ƒ} | HtmlSelectElement {items: Array(3), addItem: ƒ, removeItem: ƒ} | ||
items: (3) [1, 2, 3] | items: (3) [1, 2, 3] | ||
Line 180: | Line 180: | ||
</syntaxhighlight>'''3.''' Let's implement the Inheritance and do a test. The follow is the most important part of the exercise. | </syntaxhighlight>'''3.''' Let's implement the Inheritance and do a test. The follow is the most important part of the exercise. | ||
Here we can't use the approach described in the lesson [[JavaScript OOP Course 4: Prototypical Inheritance#Resetting the Constructor|JavaScript Prototypical Inheritance]] - e.g. <code>Html­Se­lect­Ele­ment­.­pro­to­type = Ob­ject.­cre­ate(Html­Ele­ment­.­pro­to­type);</code> Because in this case we will assign only the Prototype of <code>HtmlElement()</code> to the Prototype of <code>HtmlSelectElement()</code>. But, as we can see above, we can't inherit the <code>click()</code> method in this way, because it's defined at the <code>HtmlElement()</code> Constructor not at its prototype level. So in this case we need to:<syntaxhighlight lang="javascript" class=" | Here we can't use the approach described in the lesson [[JavaScript OOP Course 4: Prototypical Inheritance#Resetting the Constructor|JavaScript Prototypical Inheritance]] - e.g. <code>Html­Se­lect­Ele­ment­.­pro­to­type = Ob­ject.­cre­ate(Html­Ele­ment­.­pro­to­type);</code> Because in this case we will assign only the Prototype of <code>HtmlElement()</code> to the Prototype of <code>HtmlSelectElement()</code>. But, as we can see above, we can't inherit the <code>click()</code> method in this way, because it's defined at the <code>HtmlElement()</code> Constructor not at its prototype level. So in this case we need to:<syntaxhighlight lang="javascript" class="code-continue"> | ||
HtmlSelectElement.prototype = new HtmlElement(); | HtmlSelectElement.prototype = new HtmlElement(); | ||
HtmlSelectElement.prototype.constructor = HtmlSelectElement; | HtmlSelectElement.prototype.constructor = HtmlSelectElement; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="javascript" class=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
const s2 = new HtmlSelectElement(['a', 'b', 'c']); | const s2 = new HtmlSelectElement(['a', 'b', 'c']); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="javascript" class=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
console.log(s2); | console.log(s2); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="shell-session" class=" | <syntaxhighlight lang="shell-session" class="code-continue"> | ||
HtmlSelectElement {items: Array(3), addItem: ƒ, removeItem: ƒ} | HtmlSelectElement {items: Array(3), addItem: ƒ, removeItem: ƒ} | ||
items: (3) ['a', 'b', 'c'] | items: (3) ['a', 'b', 'c'] | ||
Line 202: | Line 202: | ||
constructor: ƒ HtmlElement() | constructor: ƒ HtmlElement() | ||
[[Prototype]]: Object | [[Prototype]]: Object | ||
</syntaxhighlight><syntaxhighlight lang="javascript" class=" | </syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue"> | ||
s2.click(); | s2.click(); | ||
s2.focus(); | s2.focus(); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="shell-session" class=" | <syntaxhighlight lang="shell-session" class="code-continue"> | ||
clicked | clicked | ||
focused | focused | ||
Line 212: | Line 212: | ||
== Polymorphism == | == Polymorphism == | ||
'''1.''' Continuing from the last exercise and extend <code>HtmlSelectElement()</code> and implement a <code>.render()</code> method that will output <code><select></code> element and a number of <code><option></code> elements that matching the elements from the <code>items</code> array.<syntaxhighlight lang="javascript" class=" | '''1.''' Continuing from the last exercise and extend <code>HtmlSelectElement()</code> and implement a <code>.render()</code> method that will output <code><select></code> element and a number of <code><option></code> elements that matching the elements from the <code>items</code> array.<syntaxhighlight lang="javascript" class="code-continue"> | ||
HtmlSelectElement.prototype.render = function() { | HtmlSelectElement.prototype.render = function() { | ||
if (Array.isArray(this.items) && this.items.lengtt !== 0) | if (Array.isArray(this.items) && this.items.lengtt !== 0) | ||
Line 219: | Line 219: | ||
+ '\n</select>'; | + '\n</select>'; | ||
}; | }; | ||
</syntaxhighlight><syntaxhighlight lang="javascript" class=" | </syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue"> | ||
// Note here we can use template string inside another like this: | // Note here we can use template string inside another like this: | ||
return `<select>${this.items.map(item => `\t<option>${item}</option>`).join('\n')}</select>`; | return `<select>${this.items.map(item => `\t<option>${item}</option>`).join('\n')}</select>`; | ||
</syntaxhighlight><syntaxhighlight lang="javascript" class=" | </syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue"> | ||
console.log(s3); | console.log(s3); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="shell-session" class=" | <syntaxhighlight lang="shell-session" class="code-continue"> | ||
HtmlSelectElement {items: Array(3), addItem: ƒ, removeItem: ƒ} | HtmlSelectElement {items: Array(3), addItem: ƒ, removeItem: ƒ} | ||
items: (3) ['a', 'b', 'c'] | items: (3) ['a', 'b', 'c'] | ||
Line 238: | Line 238: | ||
constructor: ƒ HtmlElement() | constructor: ƒ HtmlElement() | ||
[[Prototype]]: Object | [[Prototype]]: Object | ||
</syntaxhighlight><syntaxhighlight lang="javascript" class=" | </syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue"> | ||
console.log( s3.render() ); | console.log( s3.render() ); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="html" class=" | <syntaxhighlight lang="html" class="code-continue"> | ||
<select> | <select> | ||
<option>a</option> | <option>a</option> | ||
Line 247: | Line 247: | ||
<option>c</option> | <option>c</option> | ||
</select> | </select> | ||
</syntaxhighlight>'''2.''' Now let's create another constructor that will generate <code><img></code> elements, name it <code>HtmlImageElement()</code>. It must inherit <code>.click()</code> and <code>.focus()</code> from <code>HtmlElement()</code> and must have its own <code>.render()</code> method. The new objects must have <code>src</code> property that will be used within the <code>.render()</code> method.<syntaxhighlight lang="javascript" class=" | </syntaxhighlight>'''2.''' Now let's create another constructor that will generate <code><img></code> elements, name it <code>HtmlImageElement()</code>. It must inherit <code>.click()</code> and <code>.focus()</code> from <code>HtmlElement()</code> and must have its own <code>.render()</code> method. The new objects must have <code>src</code> property that will be used within the <code>.render()</code> method.<syntaxhighlight lang="javascript" class="code-continue"> | ||
function HtmlImageElement(src = '#') { | function HtmlImageElement(src = '#') { | ||
this.src = src; | this.src = src; | ||
Line 258: | Line 258: | ||
if (this.src.lengtt !== 0) return `<img src="${this.src}" />\n`; | if (this.src.lengtt !== 0) return `<img src="${this.src}" />\n`; | ||
} | } | ||
</syntaxhighlight><syntaxhighlight lang="javascript" class=" | </syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue"> | ||
const img = new HtmlImageElement('https://metalevel.tech'); | const img = new HtmlImageElement('https://metalevel.tech'); | ||
</syntaxhighlight><syntaxhighlight lang="javascript" class=" | </syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue"> | ||
console.log(img); | console.log(img); | ||
</syntaxhighlight><syntaxhighlight lang="shell-session" class=" | </syntaxhighlight><syntaxhighlight lang="shell-session" class="code-continue"> | ||
HtmlImageElement {src: 'https://metalevel.tech'} | HtmlImageElement {src: 'https://metalevel.tech'} | ||
src: "https://metalevel.tech" | src: "https://metalevel.tech" | ||
Line 273: | Line 273: | ||
constructor: ƒ HtmlElement() | constructor: ƒ HtmlElement() | ||
[[Prototype]]: Object | [[Prototype]]: Object | ||
</syntaxhighlight><syntaxhighlight lang="javascript" class=" | </syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue"> | ||
img.click(); | img.click(); | ||
img.focus(); | img.focus(); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="shell-session" class=" | <syntaxhighlight lang="shell-session" class="code-continue"> | ||
clicked | clicked | ||
focused | focused | ||
</syntaxhighlight><syntaxhighlight lang="javascript" class=" | </syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue"> | ||
console.log(img.render()); | console.log(img.render()); | ||
</syntaxhighlight><syntaxhighlight lang="html" class=" | </syntaxhighlight><syntaxhighlight lang="html" class="code-continue"> | ||
<img src="https://metalevel.tech" /> | <img src="https://metalevel.tech" /> | ||
</syntaxhighlight>'''3.''' Finally create an array of two or more HTML Select and Image elements and print to the console the output of the <code>.render()</code> method of each of them. <syntaxhighlight lang="javascript" class=" | </syntaxhighlight>'''3.''' Finally create an array of two or more HTML Select and Image elements and print to the console the output of the <code>.render()</code> method of each of them. <syntaxhighlight lang="javascript" class="code-continue"> | ||
const elements = [ | const elements = [ | ||
new HtmlSelectElement(['1', '2', '3']), | new HtmlSelectElement(['1', '2', '3']), | ||
new HtmlImageElement('https://szs.space') | new HtmlImageElement('https://szs.space') | ||
]; | ]; | ||
</syntaxhighlight><syntaxhighlight lang="javascript" class=" | </syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue"> | ||
for (let element of elements) | for (let element of elements) | ||
console.log(element.render()); | console.log(element.render()); | ||
</syntaxhighlight><syntaxhighlight lang="html" class=" | </syntaxhighlight><syntaxhighlight lang="html" class="code-continue"> | ||
<select> | <select> | ||
<option>1</option> | <option>1</option> | ||
Line 309: | Line 309: | ||
* <code>count</code> return the length of the stack, | * <code>count</code> return the length of the stack, | ||
* <code>show</code> print the stack. | * <code>show</code> print the stack. | ||
<syntaxhighlight lang="javascript" class=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
const _items = new WeakMap(); | const _items = new WeakMap(); | ||
</syntaxhighlight><syntaxhighlight lang="javascript" class=" | </syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue"> | ||
class Stack { | class Stack { | ||
constructor() { | constructor() { | ||
Line 343: | Line 343: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="javascript" class=" | <syntaxhighlight lang="javascript" class="code-continue"> | ||
stack; | stack; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 360: | Line 360: | ||
push: ƒ push(obj) | push: ƒ push(obj) | ||
[[Prototype]]: Object | [[Prototype]]: Object | ||
</syntaxhighlight><syntaxhighlight lang="javascript" class=" | </syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue"> | ||
stack.push('a'); | stack.push('a'); | ||
stack.push('b'); | stack.push('b'); | ||
Line 366: | Line 366: | ||
stack.count; | stack.count; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="shell-session" class=" | <syntaxhighlight lang="shell-session" class="code-continue"> | ||
3 | 3 | ||
</syntaxhighlight><syntaxhighlight lang="javascript" class=" | </syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue"> | ||
stack.pop(); | stack.pop(); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="shell-session" class=" | <syntaxhighlight lang="shell-session" class="code-continue"> | ||
'c' | 'c' | ||
</syntaxhighlight><syntaxhighlight lang="javascript" class=" | </syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue"> | ||
stack.peek(); | stack.peek(); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="shell-session" class=" | <syntaxhighlight lang="shell-session" class="code-continue"> | ||
'b' | 'b' | ||
</syntaxhighlight><syntaxhighlight lang="javascript" class=" | </syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue"> | ||
stack.show; | stack.show; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="shell-session" class=" | <syntaxhighlight lang="shell-session" class="code-continue"> | ||
(2) ['a', 'b'] | (2) ['a', 'b'] | ||
</syntaxhighlight> | </syntaxhighlight> |
Latest revision as of 07:28, 26 September 2022
References
- Code with Mosh: The Ultimate JavaScript Mastery Series – Part 2
- W3School: JavaScript Tutorial
Stopwatch
Use Constructor function and create an Object sw
that has the following members:
- Properties:
duration
- Method:
reset()
,start()
,stop()
.
function Stopwatch() {
let startTime = null; let endTime = null; let running = false; let duration = 0;
this.start = function() {
if (running) throw new Error('The Stopwatch is already startrd.');
running = true;
startTime = new Date();
};
this.stop = function() {
if (!running) throw new Error('The Stopwatch is not running.');
running = false;
endTime = new Date();
const seconds = (endTime.getTime() - startTime.getTime()) / 1000;
duration += seconds;
};
this.reset = function() {
startTime = null; endTime = null; running = false; duration = 0;
};
Object.defineProperty(this, 'duration', {
get: function() { return duration; }
});
}
const sw = new Stopwatch();
sw.start();
sw.stop();
sw.duration;
8.56
sw.reset();
Stopwatch (move all methods at prototype level)
In order to do that we need to give public access to the private properties (defined by the let
keyword) as it is done below, but note this breaks the abstraction principle, for example: In order to set the duration
value from the prototype level we need to create setter that private variable, but in the same time this setter function is accessible from outside… This example is designed to show that sometimes it is a bad practice to move some members at the prototype level – Premature optimization is the root of all evils.
function Stopwatch() {
let duration = 0; let startTime = null; let endTime = null; let running = false;
Object.defineProperty(this, 'duration', {
get: function() { return duration; },
set: function(value) { duration = value; }
});
Object.defineProperty(this, 'startTime', {
get: function() { return startTime; },
set: function(value) { startTime = value; }
});
Object.defineProperty(this, 'endTime', {
get: function() { return endTime; },
set: function(value) { endTime = value; }
});
Object.defineProperty(this, 'running', {
get: function() { return running; },
set: function(value) { running = value; }
});
}
Stopwatch.prototype.start = function() {
if (this.running) throw new Error('The Stopwatch is already started.');
this.running = true;
this.startTime = new Date();
};
Stopwatch.prototype.stop = function() {
if (!this.running) throw new Error('The Stopwatch not started.');
this.running = false;
this.endTime = new Date;
const seconds = (this.endTime.getTime() - this.startTime.getTime()) / 1000;
this.duration += seconds;
};
Stopwatch.prototype.reset = function() {
this.duration = 0;
this.startTime = null;
this.endTime = null;
this.running = false;
};
Prototypical Inheritance
Create two constructors HtmlElement()
and HtmlSelectElement()
with the following specification:
HtmlElement()
should have two methods:
click()
that will produce the text 'clicked' in the console and is defined at the Constructor function level.focus()
that will produce the text 'focused' in the console and is defined at the Prototype level of the Constructor function.
HtmlSelectElement()
should have the following members:
- A property called
items
which is an array that could be an empty array or array passed as argument to the constructor function when a new object is created by it. - Two methods:
addItem(item)
that will add the item passed as argument to the array ofitems
.removeItem(item)
that will remove the item passed as argument from the array ofitems
.
- Also should Prototypically Inherit the two methods –
click()
andfocus()
– from theHtmlElement()
constructor.
1. Let's create and test HtmlElement()
.
function HtmlElement() {
this.click = function() {
console.log('clicked');
};
}
HtmlElement.prototype.focus = function() {
console.log('focused');
};
const e = new HtmlElement();
console.log(e);
HtmlElement {click: ƒ}
click: ƒ ()
[[Prototype]]: Object
focus: ƒ ()
constructor: ƒ HtmlElement()
[[Prototype]]: Object
e.click();
e.focus();
clicked
focused
2. Let's create and test the basic implementation of the HtmlSelectElement()
constructor.
function HtmlSelectElement(...args) {
if (Array.isArray(args[0])) this.items = args[0];
else this.items = args;
this.addItem = function(item) {
this.items.push(item);
};
this.removeItem = function(item) {
let reducedItems = [];
for (let thisItem of this.items) if (thisItem !== item) reducedItems.push(thisItem);
this.items = reducedItems;
};
}
const s1 = new HtmlSelectElement([1, 2, 3]);
console.log(s1);
HtmlSelectElement {items: Array(3), addItem: ƒ, removeItem: ƒ}
items: (3) [1, 2, 3]
addItem: ƒ (item)
removeItem: ƒ (item)
[[Prototype]]: Object
constructor: ƒ HtmlSelectElement(...args)
[[Prototype]]: Object
3. Let's implement the Inheritance and do a test. The follow is the most important part of the exercise.
Here we can't use the approach described in the lesson JavaScript Prototypical Inheritance – e.g. HtmlSelectElement.prototype = Object.create(HtmlElement.prototype);
Because in this case we will assign only the Prototype of HtmlElement()
to the Prototype of HtmlSelectElement()
. But, as we can see above, we can't inherit the click()
method in this way, because it's defined at the HtmlElement()
Constructor not at its prototype level. So in this case we need to:
HtmlSelectElement.prototype = new HtmlElement();
HtmlSelectElement.prototype.constructor = HtmlSelectElement;
const s2 = new HtmlSelectElement(['a', 'b', 'c']);
console.log(s2);
HtmlSelectElement {items: Array(3), addItem: ƒ, removeItem: ƒ}
items: (3) ['a', 'b', 'c']
addItem: ƒ (item)
removeItem: ƒ (item)
[[Prototype]]: HtmlElement
click: ƒ ()
constructor: ƒ HtmlSelectElement(...args)
[[Prototype]]: Object
focus: ƒ ()
constructor: ƒ HtmlElement()
[[Prototype]]: Object
s2.click();
s2.focus();
clicked
focused
Polymorphism
1. Continuing from the last exercise and extend HtmlSelectElement()
and implement a .render()
method that will output <select>
element and a number of <option>
elements that matching the elements from the items
array.
HtmlSelectElement.prototype.render = function() {
if (Array.isArray(this.items) && this.items.lengtt !== 0)
return '<select>\n'
+ this.items.map(item => `\t<option>${item}</option>`).join('\n')
+ '\n</select>';
};
// Note here we can use template string inside another like this:
return `<select>${this.items.map(item => `\t<option>${item}</option>`).join('\n')}</select>`;
console.log(s3);
HtmlSelectElement {items: Array(3), addItem: ƒ, removeItem: ƒ}
items: (3) ['a', 'b', 'c']
addItem: ƒ (item)
removeItem: ƒ (item)
[[Prototype]]: HtmlElement
click: ƒ ()
render: ƒ ()
constructor: ƒ HtmlSelectElement(...args)
[[Prototype]]: Object
focus: ƒ ()
constructor: ƒ HtmlElement()
[[Prototype]]: Object
console.log( s3.render() );
<select>
<option>a</option>
<option>b</option>
<option>c</option>
</select>
2. Now let's create another constructor that will generate <img>
elements, name it HtmlImageElement()
. It must inherit .click()
and .focus()
from HtmlElement()
and must have its own .render()
method. The new objects must have src
property that will be used within the .render()
method.
function HtmlImageElement(src = '#') {
this.src = src;
}
HtmlImageElement.prototype = new HtmlElement();
HtmlImageElement.prototype.constructor = HtmlImageElement;
HtmlImageElement.prototype.render = function() {
if (this.src.lengtt !== 0) return `<img src="${this.src}" />\n`;
}
const img = new HtmlImageElement('https://metalevel.tech');
console.log(img);
HtmlImageElement {src: 'https://metalevel.tech'}
src: "https://metalevel.tech"
[[Prototype]]: HtmlElement
click: ƒ ()
render: ƒ ()
constructor: ƒ HtmlImageElement(src = '#')
[[Prototype]]: Object
focus: ƒ ()
constructor: ƒ HtmlElement()
[[Prototype]]: Object
img.click();
img.focus();
clicked
focused
console.log(img.render());
<img src="https://metalevel.tech" />
3. Finally create an array of two or more HTML Select and Image elements and print to the console the output of the .render()
method of each of them.
const elements = [
new HtmlSelectElement(['1', '2', '3']),
new HtmlImageElement('https://szs.space')
];
for (let element of elements)
console.log(element.render());
<select>
<option>1</option>
<option>2</option>
<option>3</option>
</select>
<img src="https://szs.space" />
Create Stack with ES6 Classes
By using ES6 Classes, implement a LIFO Stack that has the following members:
push()
add element to the stack,pop()
remove element from the stack,peek()
show the element on top,count
return the length of the stack,show
print the stack.
const _items = new WeakMap();
class Stack {
constructor() {
_items.set(this, []);
}
get show() { return _items.get(this); }
get count() { return _items.get(this).length; }
push(obj) {
const items = _items.get(this);
if (obj === undefined) throw new Error('Invalid value.');
items.push(obj);
}
pop() {
const items = _items.get(this);
if (items.length === 0) throw new Error('Stack is empty.');
return items.pop();
}
peek() {
const items = _items.get(this);
if (items.length === 0) throw new Error('Stack is empty.');
return items[items.length - 1];
}
}
const stack = new Stack();
stack;
Stack {}
count: 0
show: Array(0)
[[Prototype]]: Object
constructor: class Stack
count: 0
show: Array(0)
get count: ƒ count()
get show: ƒ show()
peek: ƒ peek()
pop: ƒ pop()
push: ƒ push(obj)
[[Prototype]]: Object
stack.push('a');
stack.push('b');
stack.push('c');
stack.count;
3
stack.pop();
'c'
stack.peek();
'b'
stack.show;
(2) ['a', 'b']