1. Convertidor de bytes
Convertidor de bytes. Programa que convierte prefijos de bytes del sistema internacional (base 10) a base 2, y viceversa.
1.1. Contenido XHTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="author" content="Jeisson Hidalgo-Céspedes"/>
<meta name="copyright" content="(C) 2011 ECCI-UCR"/>
<meta name="keywords" content="byte converter, SI"/>
<title>Byte converter</title>
<link href="css/byte_converter.css" rel="stylesheet"/>
<!-- embedded css/js -->
<style>
body { font-family: Helvetica, sans-serif; }
/* nav > ul > li { } */
</style>
</head>
<body>
<main>
<p>
<span id="input_size_fields">
<label for="input_size">Size:</label>
<input type="text" id="input_size" name="input_size" value="0"/>
</span>
<select id="unit" name="unit">
<optgroup label="base 10">
<option selected="selected">B</option>
<option>kB</option>
<option>MB</option>
<option>GB</option><!-- value="3" -->
<option>TB</option>
<option>PB</option>
<option>EB</option>
</optgroup>
<optgroup label="base 2">
<option>B</option>
<option>kiB</option>
<option>MiB</option>
<option>GiB</option><!-- value="3" -->
<option>TiB</option>
<option>PiB</option>
<option>EiB</option>
</optgroup>
</select>
<label for="decimals">Decimals:</label>
<input type="number" id="decimals" name="decimals" value="7" min="0" max="15"/>
<button id="convert_button">Convert</button>
<!-- inline: style="color: red" onclick="alert('Starting conversion');" -->
</p>
<p id="log" class="error log"></p>
<table id="output">
<thead>
<tr>
<th colspan="2">Decimal</th>
<th colspan="2">Binary</th>
</tr>
</thead>
<tbody>
<tr><td>0</td><td>B</td><td>0</td><td>B</td></tr>
<tr><td>0</td><td>kB</td><td>0</td><td>kiB</td></tr>
<tr><td>0</td><td>MB</td><td>0</td><td>MiB</td></tr>
<tr><td>0</td><td>GB</td><td>0</td><td>GiB</td></tr>
<tr><td>0</td><td>TB</td><td>0</td><td>TiB</td></tr>
<tr><td>0</td><td>PB</td><td>0</td><td>PiB</td></tr>
<tr><td>0</td><td>EB</td><td>0</td><td>EiB</td></tr>
</tbody>
</table>
</main>
<!-- defer="defer" async="async" -->
<script>
// console.log('embedded script was executed');
</script>
<script async="async" src="js/byte_converter.js"/>
</body>
</html>
1.2. Presentación CSS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/* @import url('common.css'); */
table
{
/* border: 1px solid; */
border-collapse: collapse;
width: 100%;
min-width: 120px;
max-width: 800px;
}
th
{
/* border: 1px solid red; */
/* width: 50%; */
}
td
{
border: 1px solid gray;
}
.error
{
color: red;
}
1.3. Comportamiento JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/*
let element = document.getElementById(id); // #id
let elements[] = document.getElementByClassName(class); // .class
let elements[] = document.getElementByTagName(element); // element
let elements[] = document.querySelectorAll("nav > ul > li"); // element
*/
/*
const exponent = (multiplier, rowIndex) => { return multiplier * rowIndex; }
const exponent = (multiplier, rowIndex) => multiplier * rowIndex;
const m = (x, y) => x * y;
*/
function exponent(multiplier, rowIndex) {
return multiplier * rowIndex;
}
function power(base, multiplier, rowIndex) {
return base ** exponent(multiplier, rowIndex);
}
function convertFromBytes(inputValue, base, multiplier, rowIndex) {
return inputValue / power(base, multiplier, rowIndex);
}
function convertToBytes(inputValue, base, multiplier, rowIndex) {
return inputValue * power(base, multiplier, rowIndex);
}
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
// const mapRowIndex = [0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6];
/*
const game = {
score: 0,
}
*/
const mapRowIndex = [
{ base: 10, multiplier: 3, index: 0 },
{ base: 10, multiplier: 3, index: 1 },
{ base: 10, multiplier: 3, index: 2 },
{ base: 10, multiplier: 3, index: 3 },
{ base: 10, multiplier: 3, index: 4 },
{ base: 10, multiplier: 3, index: 5 },
{ base: 10, multiplier: 3, index: 6 },
{ base: 2, multiplier: 10, index: 0 },
{ base: 2, multiplier: 10, index: 1 },
{ base: 2, multiplier: 10, index: 2 },
{ base: 2, multiplier: 10, index: 3 },
{ base: 2, multiplier: 10, index: 4 },
{ base: 2, multiplier: 10, index: 5 },
{ base: 2, multiplier: 10, index: 6 },
];
/*
function mapRowIndex(selectedIndex)
{
switch ( selectedIndex )
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
return selectedIndex;
case 7:
return 0;
case 8:
return 1;
case 9:
return 2;
default:
console.assert(false);
return undefined;
}
}
*/
// procedure
// const updateCellValue = function (inputSize, cell, base, multiplier, rowIndex) {
function updateCellValue(inputSize, cell, base, multiplier, rowIndex) {
const decimals = parseInt(document.getElementById('decimals').value, 10);
console.log('unit.value = ', document.getElementById('unit').value);
console.log('unit.selectedIndex = ', document.getElementById('unit').selectedIndex);
const selectedIndex = document.getElementById('unit').selectedIndex;
const mapObject = mapRowIndex[selectedIndex];
console.assert(mapObject);
const bytes = convertToBytes(inputSize, mapObject['base'], mapObject.multiplier, mapObject.index);
console.log('bytes = ', bytes);
const convertedSize = convertFromBytes(bytes, base, multiplier, rowIndex);
cell.textContent = convertedSize.toFixed(decimals);
}
function updateRow(inputSize, rowIndex, row) {
updateCellValue(inputSize, row.cells[0], 10, 3, rowIndex);
updateCellValue(inputSize, row.cells[2], 2, 10, rowIndex);
}
function fetchInputSize() {
const inputSizeText = document.getElementById('input_size');
const inputSizeFields = document.getElementById('input_size_fields');
// inputSizeText.className = 'error';
const inputSize = parseFloat(inputSizeText.value);
const log = document.getElementById('log');
if (Number.isNaN(inputSize)) {
inputSizeFields.classList.add('error');
log.innerText = 'Invalid input size';
} else {
log.innerText = '';
inputSizeFields.classList.remove('error');
}
return inputSize;
}
// procedure
function convertAll(/* event */) {
const table = document.getElementById('output');
const inputSize = fetchInputSize();
if (Number.isNaN(inputSize)) {
return;
}
// table.innerHTML = `<tbody class="error"><tr>${2 * inputSize.value.valueOf()}</tr></tbody>`;
// Row 0 contains the table head with Decimal and Binary headers
// Therefore we start at row 1
for (let rowIndex = 1; rowIndex < table.rows.length; rowIndex += 1) {
updateRow(inputSize, rowIndex - 1, table.rows[rowIndex]);
}
/*
const tr = document.createElement('tr');
const td = document.createElement('td');
td.textContent = inputSize.value;
tr.appendChild(td);
table.tBodies[0].appendChild(tr);
*/
// console.log('inputSize.value = ', inputSize.value);
}
function setupConverter() {
const inputSize = document.getElementById('input_size');
console.assert(inputSize);
inputSize.addEventListener('input', convertAll);
const decimals = document.getElementById('decimals');
console.assert(decimals);
decimals.addEventListener('input', convertAll);
const convertButton = document.getElementById('convert_button');
console.assert(convertButton);
convertButton.addEventListener('click', convertAll);
const unitSelect = document.getElementById('unit');
console.assert(unitSelect);
unitSelect.addEventListener('input', convertAll);
}
// console.log(inputSize);
// console.dir(inputSize);
// document.body.onload = setupConverter
window.addEventListener('load', setupConverter);
/*
export { convert, setupConverter };
// en otro.js
import {convert} from './byte_converter.js';
*/
2. Importar/exportar JS del lado del cliente
Un archivo .js en ES6 es un módulo, donde todas las declaraciones son privadas. Si se quiere compartir algún símbolo (función, clase, constante…), debe exportarse de forma explícita (equivalente a hacerlo público):
1
2
3
4
import { theme, difficulty } from './consts.js';
console.log('theme = ', theme);
console.log('difficulty = ', difficulty);
El módulo .js que importa declara cuáles de esos símbolos exportados quiere usar:
1
2
3
4
import { theme, difficulty } from './consts.js';
console.log('theme = ', theme);
console.log('difficulty = ', difficulty);
El navegador solicitará el archivo importado './consts.js'
de la línea 1 por una solicitud HTTP, por lo que el recurso debe ser servido por un servidor web (nginx, Apache, http-server…). Se debe solicitar a los navegadores tratar el archivo .js como un módulo (línea 13):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html lang="es" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="author" content="Jeisson Hidalgo-Céspedes"/>
<title>Import</title>
</head>
<body>
<main>
<p>Import test. See your browser's developer tools.</p>
</main>
<script src="game.js" type="module"></script>
</body>
</html>
Sin embargo, WebKit (Chrome, Safari, Opera…) no soporta módulos desde archivos XHTML, aunque sí de HTML. Se puede crear un enlace simbólico que permite a la persona desarrolladora trabajar con ambas extensiones, por ejemplo:
1
2
3
4
$ ln -s import.xhtml import.html
$ ls -l import*
lrwxr-xr-x 1 12 Sep 22 17:41 import.html -> import.xhtml
-rw-r--r-- 1 408 Sep 22 17:42 import.xhtml
3. Protocolo de paso de mensajes
Imagen generada durante la presentación del protocolo de paso de mensajes (2020-set-24):
4. Objetos y clases en JavaScript
Representación en la memoria de los objetos de JavaScript: arreglos asociativos, y la propiedad prototype
.
4.1. 0. Código inicial
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
methods = {
greatestCommonDivisor(num1, num2) {
let a = Math.abs(num1);
let b = Math.abs(num2);
while (b > 0) {
const t = b;
b = a % b;
a = t;
}
return a;
},
simplify() {
const gcd = this.greatestCommonDivisor(this.numerator, this.denominator);
this.numerator /= gcd;
this.denominator /= gcd;
if (this.denominator < 0) {
this.numerator *= -1;
this.denominator *= -1;
}
},
toString() {
return this.numerator + String('/') + this.denominator;
},
add(other) {
return Fraction(
this.numerator * other.denominator + this.denominator * other.numerator,
this.denominator * other.denominator,
);
},
};
const fr1 = Fraction(12, -8); // -3/2
const fr2 = Fraction(-10, -20); // 1/2
const fr3 = fr1.add(fr2); // -2/2 = -1
const fr4 = fr3.add(Fraction(5)); // -1/1 + 5/1 = 4/1
console.log(`${fr1} + ${fr2} == ${fr3}`);
console.log(`${fr3} + 5 == ${fr4}`);
4.2. 1. Función fábrica
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// Factory function
function Fraction(numerator, denominator = 1) {
// const fraction = { };
// fraction.prototype = Fraction.methods;
const fraction = Object.create(Fraction.methods);
// Each object has its own property
fraction.numerator = numerator;
fraction.denominator = denominator;
// All objects would have a copy of this function
// fraction.toString = function toString() {
// return `${this.numerator}/${this.denominator}`;
// };
fraction.simplify();
return fraction;
}
Fraction.methods = {
toString: function toString() {
return `${this.numerator}/${this.denominator}`;
},
add: function add(other) {
return Fraction(
this.numerator * other.denominator + this.denominator * other.numerator,
this.denominator * other.denominator,
);
},
greatestCommonDivisor(num1, num2) {
let a = Math.abs(num1);
let b = Math.abs(num2);
while (b > 0) {
const t = b;
b = a % b;
a = t;
}
return a;
},
simplify() {
const gcd = this.greatestCommonDivisor(this.numerator, this.denominator);
this.numerator /= gcd;
this.denominator /= gcd;
if (this.denominator < 0) {
this.numerator *= -1;
this.denominator *= -1;
}
},
}; // static variable
const fr1 = Fraction(12, -8); // -3/2
const fr2 = Fraction(-10, -20); // 1/2
// fr2.toString = function() { return 'jiji'; }
const fr3 = fr1.add(fr2); // -2/2 = -1
const fr4 = fr3.add(Fraction(5)); // -1/1 + 5/1 = 4/1
console.log(`${fr1.toString()} + ${fr2} == ${fr3}`);
// Fraction.methods.toString = function() { return 'grr'; }
console.log(`${fr3} + 5 == ${fr4}`);
4.3. 2. Pseudoclase: función constructura + prototipo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// Constructor function -> Pseudoclass
function Fraction(numerator, denominator = 1) {
// const fraction = { };
// fraction.prototype = Fraction.prototype;
if (!new.target) {
throw Error("Fraction must called with new operator");
}
// Each object has its own property
this.numerator = numerator;
this.denominator = denominator;
this.simplify();
}
Fraction.prototype = {
toString: function toString() {
return `${this.numerator}/${this.denominator}`;
},
add: function add(other) {
return new Fraction(
this.numerator * other.denominator + this.denominator * other.numerator,
this.denominator * other.denominator,
);
},
greatestCommonDivisor(num1, num2) {
let a = Math.abs(num1);
let b = Math.abs(num2);
while (b > 0) {
const t = b;
b = a % b;
a = t;
}
return a;
},
simplify() {
const gcd = this.greatestCommonDivisor(this.numerator, this.denominator);
this.numerator /= gcd;
this.denominator /= gcd;
if (this.denominator < 0) {
this.numerator *= -1;
this.denominator *= -1;
}
},
}; // static variable
const fr1 = new Fraction(12, -8); // -3/2
const fr2 = new Fraction(-10, -20); // 1/2
// fr2.toString = function() { return 'jiji'; }
const fr3 = fr1.add(fr2); // -2/2 = -1
const fr4 = fr3.add(new Fraction(5)); // -1/1 + 5/1 = 4/1
console.log(`${fr1.toString()} + ${fr2} == ${fr3}`);
// Fraction.methods.toString = function() { return 'grr'; }
console.log(`${fr3} + 5 == ${fr4}`);
4.4. 3. Clases en ES6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// Class
class Fraction {
constructor(numerator, denominator = 1) {
// Each object has its own property
this.numerator = numerator;
this.denominator = denominator;
this.simplify();
}
toString() {
return `${this.numerator}/${this.denominator}`;
}
add(other) {
return new Fraction(
this.numerator * other.denominator + this.denominator * other.numerator,
this.denominator * other.denominator,
);
}
static greatestCommonDivisor(num1, num2) {
let a = Math.abs(num1);
let b = Math.abs(num2);
while (b > 0) {
const t = b;
b = a % b;
a = t;
}
return a;
}
simplify() {
const gcd = Fraction.greatestCommonDivisor(this.numerator, this.denominator);
this.numerator /= gcd;
this.denominator /= gcd;
if (this.denominator < 0) {
this.numerator *= -1;
this.denominator *= -1;
}
}
}
const fr1 = new Fraction(12, -8); // -3/2
const fr2 = new Fraction(-10, -20); // 1/2
// fr2.toString = function() { return 'jiji'; }
const fr3 = fr1.add(fr2); // -2/2 = -1
const fr4 = fr3.add(new Fraction(5)); // -1/1 + 5/1 = 4/1
console.log(`${fr1.toString()} + ${fr2} == ${fr3}`);
// Fraction.methods.toString = function() { return 'grr'; }
console.log(`${fr3} + 5 == ${fr4}`);
4.5. 4. Getters/setters
Invariante: el denominador nunca puede ser cero.
1
(Pendiente)
4.6. 5. Herencia
Fracción combinada (MixedFraction), ej.: 4⅔
1
(Pendiente)
4.7. 6. Características en estandarización
-
Atributos: declarados en la clase y no el constructor.
-
Miembros privados/protegidos: inician con
#
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// Class
class Fraction {
#numerator = 0;
// #denominator = 1;
static count = 0;
constructor(numerator, denominator = 1) {
// Each object has its own property
this.numerator = numerator;
this.#denominator = denominator;
this.simplify();
}
toString() {
return `${this.numerator}/${this.denominator}`;
}
add(other) {
return new Fraction(
this.numerator * other.denominator + this.denominator * other.numerator,
this.denominator * other.denominator,
);
}
static greatestCommonDivisor(num1, num2) {
let a = Math.abs(num1);
let b = Math.abs(num2);
while (b > 0) {
const t = b;
b = a % b;
a = t;
}
return a;
}
simplify() {
const gcd = Fraction.greatestCommonDivisor(this.numerator, this.denominator);
this.numerator /= gcd;
this.denominator /= gcd;
if (this.denominator < 0) {
this.numerator *= -1;
this.denominator *= -1;
}
}
}
const fr1 = new Fraction(12, -8); // -3/2
const fr2 = new Fraction(-10, -20); // 1/2
// fr2.toString = function() { return 'jiji'; }
const fr3 = fr1.add(fr2); // -2/2 = -1
const fr4 = fr3.add(new Fraction(5)); // -1/1 + 5/1 = 4/1
console.log(`${fr1.toString()} + ${fr2} == ${fr3}`);
// Fraction.methods.toString = function() { return 'grr'; }
console.log(`${fr3} + 5 == ${fr4}`);