JavaScript OOP Course 9: Exercises: Difference between revisions

From WikiMLT
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="mlw-continue">
<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="mlw-continue">
<syntaxhighlight lang="javascript" class="code-continue">
const sw = new Stopwatch();
const sw = new Stopwatch();
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="javascript" class="mlw-continue">
<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="mlw-continue">
<syntaxhighlight lang="shell-session" class="code-continue">
8.56
8.56
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="javascript" class="mlw-continue">
<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="mlw-continue">
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="mlw-continue">
'''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="mlw-continue">
<syntaxhighlight lang="javascript" class="code-continue">
const e = new HtmlElement();
const e = new HtmlElement();
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="javascript" class="mlw-continue">
<syntaxhighlight lang="javascript" class="code-continue">
console.log(e);
console.log(e);
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="shell-session" class="mlw-continue">
<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="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
e.click();
e.click();
e.focus();
e.focus();
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="shell-session" class="mlw-continue">
<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="mlw-continue">
</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="mlw-continue">
<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="mlw-continue">
<syntaxhighlight lang="javascript" class="code-continue">
console.log(s1);
console.log(s1);
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="shell-session" class="mlw-continue">
<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&shy;Se&shy;lect&shy;Ele&shy;ment&shy;.&shy;pro&shy;to&shy;type = Ob&shy;ject.&shy;cre&shy;ate(Html&shy;Ele&shy;ment&shy;.&shy;pro&shy;to&shy;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="mlw-continue">
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&shy;Se&shy;lect&shy;Ele&shy;ment&shy;.&shy;pro&shy;to&shy;type = Ob&shy;ject.&shy;cre&shy;ate(Html&shy;Ele&shy;ment&shy;.&shy;pro&shy;to&shy;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="mlw-continue">
<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="mlw-continue">
<syntaxhighlight lang="javascript" class="code-continue">
console.log(s2);
console.log(s2);
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="shell-session" class="mlw-continue">
<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="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
s2.click();
s2.click();
s2.focus();
s2.focus();
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="shell-session" class="mlw-continue">
<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="mlw-continue">
'''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="mlw-continue">
</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="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
console.log(s3);
console.log(s3);
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="shell-session" class="mlw-continue">
<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="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
console.log( s3.render() );
console.log( s3.render() );
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="html" class="mlw-continue">
<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="mlw-continue">
</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="mlw-continue">
</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="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
console.log(img);
console.log(img);
</syntaxhighlight><syntaxhighlight lang="shell-session" class="mlw-continue">
</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="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
img.click();
img.click();
img.focus();
img.focus();
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="shell-session" class="mlw-continue">
<syntaxhighlight lang="shell-session" class="code-continue">
clicked
clicked
focused
focused
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
console.log(img.render());
console.log(img.render());
</syntaxhighlight><syntaxhighlight lang="html" class="mlw-continue">
</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="mlw-continue">
</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="mlw-continue">
</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="mlw-continue">
</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="mlw-continue">
<syntaxhighlight lang="javascript" class="code-continue">
const _items = new WeakMap();
const _items = new WeakMap();
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
class Stack {
class Stack {
     constructor() {
     constructor() {
Line 343: Line 343:
</syntaxhighlight>
</syntaxhighlight>


<syntaxhighlight lang="javascript" class="mlw-continue">
<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="mlw-continue">
</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="mlw-continue">
<syntaxhighlight lang="shell-session" class="code-continue">
3
3
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
stack.pop();
stack.pop();
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="shell-session" class="mlw-continue">
<syntaxhighlight lang="shell-session" class="code-continue">
'c'
'c'
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
stack.peek();
stack.peek();
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="shell-session" class="mlw-continue">
<syntaxhighlight lang="shell-session" class="code-continue">
'b'
'b'
</syntaxhighlight><syntaxhighlight lang="javascript" class="mlw-continue">
</syntaxhighlight><syntaxhighlight lang="javascript" class="code-continue">
stack.show;
stack.show;
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang="shell-session" class="mlw-continue">
<syntaxhighlight lang="shell-session" class="code-continue">
(2) ['a', 'b']
(2) ['a', 'b']
</syntaxhighlight>
</syntaxhighlight>

Latest revision as of 08:28, 26 September 2022

Ref­er­ences

Stop­watch

Use Con­struc­tor func­tion and cre­ate an Ob­ject sw that has the fol­low­ing mem­bers:

  • Prop­er­ties: du­ra­tion
  • Method: re­set(), 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();

Stop­watch (move all meth­ods at pro­to­type lev­el)

In or­der to do that we need to give pub­lic ac­cess to the pri­vate prop­er­ties (de­fined by the let key­word) as it is done be­low, but note this breaks the ab­strac­tion prin­ci­ple, for ex­am­ple: In or­der to set the du­ra­tion val­ue from the pro­to­type lev­el we need to cre­ate set­ter that pri­vate vari­able, but in the same time this set­ter func­tion is ac­ces­si­ble from out­side… This ex­am­ple is de­signed to show that some­times it is a bad prac­tice to move some mem­bers at the pro­to­type lev­el – Pre­ma­ture op­ti­miza­tion 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;
};

Pro­to­typ­i­cal In­her­i­tance

Cre­ate two con­struc­tors Htm­lEle­ment() and HtmlS­e­lectEle­ment() with the fol­low­ing spec­i­fi­ca­tion:

Htm­lEle­ment() should have two meth­ods:

  • click() that will pro­duce the text 'clicked' in the con­sole and is de­fined at the Con­struc­tor func­tion lev­el.
  • fo­cus() that will pro­duce the text 'fo­cused' in the con­sole and is de­fined at the Pro­to­type lev­el of the Con­struc­tor func­tion.

HtmlS­e­lectEle­ment() should have the fol­low­ing mem­bers:

  • A prop­er­ty called items which is an ar­ray that could be an emp­ty ar­ray or ar­ray passed as ar­gu­ment to the con­struc­tor func­tion when a new ob­ject is cre­at­ed by it.
  • Two meth­ods:
    • addItem(item) that will add the item passed as ar­gu­ment to the ar­ray of items.
    • removeItem(item) that will re­move the item passed as ar­gu­ment from the ar­ray of items.
  • Al­so should Pro­to­typ­i­cal­ly In­her­it the two meth­ods – click() and fo­cus() – from the Htm­lEle­ment() con­struc­tor.

1. Let's cre­ate and test Htm­lEle­ment().

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 cre­ate and test the ba­sic im­ple­men­ta­tion of the HtmlS­e­lectEle­ment() con­struc­tor.

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 im­ple­ment the In­her­i­tance and do a test. The fol­low is the most im­por­tant part of the ex­er­cise. Here we can't use the ap­proach de­scribed in the les­son JavaScript Pro­to­typ­i­cal In­her­i­tance – e.g. Html­Se­lect­Ele­ment­.­pro­to­type = Ob­ject.­cre­ate(Html­Ele­ment­.­pro­to­type); Be­cause in this case we will as­sign on­ly the Pro­to­type of Htm­lEle­ment() to the Pro­to­type of HtmlS­e­lectEle­ment(). But, as we can see above, we can't in­her­it the click() method in this way, be­cause it's de­fined at the Htm­lEle­ment() Con­struc­tor not at its pro­to­type lev­el. 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

Poly­mor­phism

1. Con­tin­u­ing from the last ex­er­cise and ex­tend HtmlS­e­lectEle­ment() and im­ple­ment a .ren­der() method that will out­put <se­lect> el­e­ment and a num­ber of <op­tion> el­e­ments that match­ing the el­e­ments from the items ar­ray.

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 cre­ate an­oth­er con­struc­tor that will gen­er­ate <img> el­e­ments, name it Htm­lIm­ageEle­ment(). It must in­her­it .click() and .fo­cus() from Htm­lEle­ment() and must have its own .ren­der() method. The new ob­jects must have src prop­er­ty that will be used with­in the .ren­der() 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. Fi­nal­ly cre­ate an ar­ray of two or more HTML Se­lect and Im­age el­e­ments and print to the con­sole the out­put of the .ren­der() 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" />

Cre­ate Stack with ES6 Class­es

By us­ing ES6 Class­es, im­ple­ment a LI­FO Stack that has the fol­low­ing mem­bers:

  • push() add el­e­ment to the stack,
  • pop() re­move el­e­ment from the stack,
  • peek() show the el­e­ment on top,
  • count re­turn 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']