diff --git a/1-js/09-classes/02-class-inheritance/1-class-constructor-error/solution.md b/1-js/09-classes/02-class-inheritance/1-class-constructor-error/solution.md index 4711e4827..16a66ec95 100644 --- a/1-js/09-classes/02-class-inheritance/1-class-constructor-error/solution.md +++ b/1-js/09-classes/02-class-inheritance/1-class-constructor-error/solution.md @@ -1,6 +1,6 @@ -That's because the child constructor must call `super()`. +Eso es porque el constructor hijo debe llamar a `super()`. -Here's the corrected code: +Aqui está el código corregido: ```js run class Animal { @@ -21,7 +21,7 @@ class Rabbit extends Animal { } *!* -let rabbit = new Rabbit("White Rabbit"); // ok now +let rabbit = new Rabbit("Conejo Blanco"); // bueno ahora */!* -alert(rabbit.name); // White Rabbit +alert(rabbit.name); // Conejo Blanco ``` diff --git a/1-js/09-classes/02-class-inheritance/1-class-constructor-error/task.md b/1-js/09-classes/02-class-inheritance/1-class-constructor-error/task.md index 380a4720b..cb582ac20 100644 --- a/1-js/09-classes/02-class-inheritance/1-class-constructor-error/task.md +++ b/1-js/09-classes/02-class-inheritance/1-class-constructor-error/task.md @@ -2,11 +2,11 @@ importance: 5 --- -# Error creating an instance +# Error al crear una instancia -Here's the code with `Rabbit` extending `Animal`. +Aquí está el código de la clase `Rabbit` que extiende a`Animal`. -Unfortunately, `Rabbit` objects can't be created. What's wrong? Fix it. +Desafortunadamente, los objetos `Rabbit` no se pueden crear. ¿Que pasa? Arréglalo. ```js run class Animal { @@ -24,7 +24,7 @@ class Rabbit extends Animal { } *!* -let rabbit = new Rabbit("White Rabbit"); // Error: this is not defined +let rabbit = new Rabbit("Conejo Blanco"); // Error: esto no está definido */!* alert(rabbit.name); ``` diff --git a/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js index ca613ca5e..be2053cfc 100644 --- a/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js +++ b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js @@ -1,7 +1,7 @@ class ExtendedClock extends Clock { constructor(options) { super(options); - let { precision=1000 } = options; + let { precision = 1000 } = options; this.precision = precision; } diff --git a/1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/index.html b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/index.html index c0609858b..31c0df90f 100644 --- a/1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/index.html +++ b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/index.html @@ -7,7 +7,7 @@ clock.start(); - /* Your class should work like this: */ + /* Tu clase debería funcionar así: */ /* diff --git a/1-js/09-classes/02-class-inheritance/2-clock-class-extended/task.md b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/task.md index bbc2c6a43..2a49a45df 100644 --- a/1-js/09-classes/02-class-inheritance/2-clock-class-extended/task.md +++ b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/task.md @@ -2,14 +2,14 @@ importance: 5 --- -# Extended clock +# Reloj extendido -We've got a `Clock` class. As of now, it prints the time every second. +Tenemos una clase de 'Clock'. A partir de ahora, imprime la hora cada segundo. [js src="source.view/clock.js"] -Create a new class `ExtendedClock` that inherits from `Clock` and adds the parameter `precision` -- the number of `ms` between "ticks". Should be `1000` (1 second) by default. +Crea una nueva clase `ExtendedClock` que herede de `Clock` y agrega el parámetro `precision`: este es el número de `milisegundos` entre "tics". Debe ser `1000` (1 segundo) por defecto. -- Your code should be in the file `extended-clock.js` -- Don't modify the original `clock.js`. Extend it. +- Tu código debe estar en el archivo `extended-clock.js` +- No modifiques el `clock.js` original. Extiéndelo. diff --git a/1-js/09-classes/02-class-inheritance/3-class-extend-object/rabbit-extends-object.svg b/1-js/09-classes/02-class-inheritance/3-class-extend-object/rabbit-extends-object.svg index 8ab568291..34d783b4d 100644 --- a/1-js/09-classes/02-class-inheritance/3-class-extend-object/rabbit-extends-object.svg +++ b/1-js/09-classes/02-class-inheritance/3-class-extend-object/rabbit-extends-object.svg @@ -1,67 +1 @@ - - - - rabbit-extends-object.svg - Created with sketchtool. - - - - - call: function - bind: function - ... - - - - Function.prototype - - - - constructor - - - Object - - - Rabbit - - - - [[Prototype]] - - - - [[Prototype]] - - - constructor - - - - call: function - bind: function - ... - - - - Function.prototype - - - Rabbit - - - - [[Prototype]] - - - constructor - - - class Rabbit - - - class Rabbit extends Object - - - - \ No newline at end of file +call: function bind: function ...Function.prototypeconstructorObjectRabbit[[Prototype]][[Prototype]]constructorcall: function bind: function ...Function.prototypeRabbit[[Prototype]]constructorclass Rabbitclass Rabbit extends Object \ No newline at end of file diff --git a/1-js/09-classes/02-class-inheritance/3-class-extend-object/solution.md b/1-js/09-classes/02-class-inheritance/3-class-extend-object/solution.md index fa26ec834..0f70aebc4 100644 --- a/1-js/09-classes/02-class-inheritance/3-class-extend-object/solution.md +++ b/1-js/09-classes/02-class-inheritance/3-class-extend-object/solution.md @@ -1,14 +1,14 @@ -First, let's see why the latter code doesn't work. +Primero, veamos por qué el último código no funciona. -The reason becomes obvious if we try to run it. An inheriting class constructor must call `super()`. Otherwise `"this"` won't be "defined". +La razón se vuelve obvia si tratamos de ejecutarlo. Un constructor de clase heredado debe llamar a `super()`. De lo contrario, `"this"` no se "definirá". -So here's the fix: +Así que aquí está la solución: ```js run class Rabbit extends Object { constructor(name) { *!* - super(); // need to call the parent constructor when inheriting + super(); // necesita llamar al constructor padre al heredar */!* this.name = name; } @@ -16,66 +16,66 @@ class Rabbit extends Object { let rabbit = new Rabbit("Rab"); -alert( rabbit.hasOwnProperty('name') ); // true +alert( rabbit.hasOwnProperty('name') ); // verdadero ``` -But that's not all yet. +Pero eso no es todo aún. -Even after the fix, there's still important difference in `"class Rabbit extends Object"` versus `class Rabbit`. +Incluso después de la solución, todavía hay una diferencia importante en `"class Rabbit extends Objetc"` versus `class Rabbit`. -As we know, the "extends" syntax sets up two prototypes: +Como sabemos, la sintaxis "extends" configura dos prototipos: -1. Between `"prototype"` of the constructor functions (for methods). -2. Between the constructor functions itself (for static methods). +1. Entre el `"prototype"` de las funcionalidades del constructor (para métodos). +2. Entre las funcionalidades propias del constructor (para métodos estáticos). -In our case, for `class Rabbit extends Object` it means: +En nuestro caso, para `class Rabbit extends Object` significa:: ```js run class Rabbit extends Object {} -alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true -alert( Rabbit.__proto__ === Object ); // (2) true +alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) verdadero +alert( Rabbit.__proto__ === Object ); // (2) verdadero ``` -So `Rabbit` now provides access to static methods of `Object` via `Rabbit`, like this: +Entonces `Rabbit` ahora proporciona acceso a métodos estáticos de `Object` a través de `Rabbit`, como esto: ```js run class Rabbit extends Object {} *!* -// normally we call Object.getOwnPropertyNames +// normalmente llamamos Object.getOwnPropertyNames alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // a,b */!* ``` -But if we don't have `extends Object`, then `Rabbit.__proto__` is not set to `Object`. +Pero si no tenemos `extend Object', entonces `Rabbit.__ proto__` no está configurado como `Object`. -Here's the demo: +Aqui la demostración: ```js run class Rabbit {} -alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true -alert( Rabbit.__proto__ === Object ); // (2) false (!) -alert( Rabbit.__proto__ === Function.prototype ); // as any function by default +alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) verdadero +alert( Rabbit.__proto__ === Object ); // (2) falso (!) +alert( Rabbit.__proto__ === Function.prototype ); // como cualquier función por defecto *!* -// error, no such function in Rabbit +// error, no hay tal función en Rabbit alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // Error */!* ``` -So `Rabbit` doesn't provide access to static methods of `Object` in that case. +Entonces `Rabbit` no proporciona acceso a métodos estáticos de 'Objeto' en ese caso. -By the way, `Function.prototype` has "generic" function methods, like `call`, `bind` etc. They are ultimately available in both cases, because for the built-in `Object` constructor, `Object.__proto__ === Function.prototype`. +Por cierto, `Function.prototype` tiene métodos de función "genéricos", como `call`, `bind` etc. En última instancia, están disponibles en ambos casos, porque para el constructor incorporado `Object`, `Object.__ proto__ === Function.prototype`. -Here's the picture: +Aqui está el gráfico: ![](rabbit-extends-object.svg) -So, to put it short, there are two differences: +Entonces, para resumir, hay dos diferencias: | class Rabbit | class Rabbit extends Object | |--------------|------------------------------| -| -- | needs to call `super()` in constructor | +| -- | necesita llamar a `super()` en el constructor | | `Rabbit.__proto__ === Function.prototype` | `Rabbit.__proto__ === Object` | diff --git a/1-js/09-classes/02-class-inheritance/3-class-extend-object/task.md b/1-js/09-classes/02-class-inheritance/3-class-extend-object/task.md index ca6628edf..764aa9a84 100644 --- a/1-js/09-classes/02-class-inheritance/3-class-extend-object/task.md +++ b/1-js/09-classes/02-class-inheritance/3-class-extend-object/task.md @@ -1,12 +1,12 @@ -importance: 5 +importance: 3 --- -# Class extends Object? +# Clase extiende objeto? -As we know, all objects normally inherit from `Object.prototype` and get access to "generic" object methods like `hasOwnProperty` etc. +Como sabemos, todos los objetos normalmente heredan de `Object.prototype` y obtienen acceso a métodos de objetos "genéricos" como `hasOwnProperty`, etc.. -For instance: +Por ejemplo: ```js run class Rabbit { @@ -18,17 +18,16 @@ class Rabbit { let rabbit = new Rabbit("Rab"); *!* -// hasOwnProperty method is from Object.prototype -// rabbit.__proto__ === Object.prototype -alert( rabbit.hasOwnProperty('name') ); // true +// el método hasOwnProperty es de Object.prototype +alert( rabbit.hasOwnProperty('name') ); // verdadero */!* ``` -But if we spell it out explicitly like `"class Rabbit extends Object"`, then the result would be different from a simple `"class Rabbit"`? +Pero si lo deletreamos explícitamente como `"clase Rabbit extends Objetc"`, entonces ¿el resultado sería diferente de un simple `"class Rabbit"`? -What's the difference? +¿Cual es la diferencia? -Here's an example of such code (it doesn't work -- why? fix it?): +Aquí hay un ejemplo de dicho código (no funciona, ¿por qué? ¿Solucionarlo?): ```js class Rabbit extends Object { @@ -39,5 +38,5 @@ class Rabbit extends Object { let rabbit = new Rabbit("Rab"); -alert( rabbit.hasOwnProperty('name') ); // true +alert( rabbit.hasOwnProperty('name') ); // Error ``` diff --git a/1-js/09-classes/02-class-inheritance/animal-rabbit-extends.svg b/1-js/09-classes/02-class-inheritance/animal-rabbit-extends.svg index 7a55a5043..3471904ab 100644 --- a/1-js/09-classes/02-class-inheritance/animal-rabbit-extends.svg +++ b/1-js/09-classes/02-class-inheritance/animal-rabbit-extends.svg @@ -1,64 +1 @@ - - - - animal-rabbit-extends.svg - Created with sketchtool. - - - - - constructor: Animal - run: function - stop: function - - - - Animal.prototype - - - - constructor: Rabbit - hide: function - - - Rabbit.prototype - - - - Animal - - - - Rabbit - - - new Rabbit - - - - - [[Prototype]] - - - - [[Prototype]] - - - prototype - - - - prototype - - - name: "White Rabbit" - - - constructor - - - constructor - - - - \ No newline at end of file +constructor: Animal run: function stop: functionAnimal.prototypeconstructor: Rabbit hide: functionRabbit.prototypeAnimalRabbitnew Rabbit[[Prototype]][[Prototype]]prototypeprototypename: "White Rabbit"constructorconstructorextends \ No newline at end of file diff --git a/1-js/09-classes/02-class-inheritance/article.md b/1-js/09-classes/02-class-inheritance/article.md index d6174b195..497a57e47 100644 --- a/1-js/09-classes/02-class-inheritance/article.md +++ b/1-js/09-classes/02-class-inheritance/article.md @@ -1,9 +1,13 @@ -# Class inheritance +# Herencia de clase -Let's say we have two classes. +La herencia de clase es un método para que una clase extienda a otra clase. -`Animal`: +Entonces podemos crear una nueva funcionalidad además de la existente. + +## La palabra clave "extends" + +Digamos que tenemos la clase `Animal`: ```js class Animal { @@ -12,92 +16,62 @@ class Animal { this.name = name; } run(speed) { - this.speed += speed; - alert(`${this.name} runs with speed ${this.speed}.`); + this.speed = speed; + alert(`${this.name} corre a una velocidad de ${this.speed}.`); } stop() { this.speed = 0; - alert(`${this.name} stopped.`); + alert(`${this.name} se queda quieto.`); } } -let animal = new Animal("My animal"); +let animal = new Animal("Mi animal"); ``` -![](rabbit-animal-independent-animal.svg) - - -...And `Rabbit`: +Así es como podemos representar gráficamente el objeto `animal` y la clase `Animal`: -```js -class Rabbit extends Animal { - constructor(name) { - this.name = name; - } - hide() { - alert(`${this.name} hides!`); - } -} - -let rabbit = new Rabbit("My rabbit"); -``` - -![](rabbit-animal-independent-rabbit.svg) - - -Right now they are fully independent. +![](rabbit-animal-independent-animal.svg) -But we'd want `Rabbit` to extend `Animal`. In other words, rabbits should be based on animals, have access to methods of `Animal` and extend them with its own methods. +...Y nos gustaría crear otra clase `Rabbit`. -To inherit from another class, we should specify `"extends"` and the parent class before the brackets `{..}`. +Como los conejos son animales, la clase 'Rabbit' debe basarse en 'Animal', tener acceso a métodos animales, para que los conejos puedan hacer lo que los animales "genéricos" pueden hacer. -Here `Rabbit` inherits from `Animal`: +La sintaxis para extender otra clase es: `class Child extends Parent`. -```js run -class Animal { - constructor(name) { - this.speed = 0; - this.name = name; - } - run(speed) { - this.speed += speed; - alert(`${this.name} runs with speed ${this.speed}.`); - } - stop() { - this.speed = 0; - alert(`${this.name} stopped.`); - } -} +Construyamos la clase `Rabbit` que herede de `Animal`: +```js *!* -// Inherit from Animal class Rabbit extends Animal { +*/!* hide() { - alert(`${this.name} hides!`); + alert(`${this.name} se esconde!`); } } -*/!* -let rabbit = new Rabbit("White Rabbit"); +let rabbit = new Rabbit("Conejo Blanco"); -rabbit.run(5); // White Rabbit runs with speed 5. -rabbit.hide(); // White Rabbit hides! +rabbit.run(5); // Conejo Blanco corre a una velocidad de 5. +rabbit.hide(); // Conejo Blanco se esconde! ``` -Now the `Rabbit` code became a bit shorter, as it uses `Animal` constructor by default, and it also can `run`, as animals do. +Los objetos de la clase `Rabbit` tienen acceso a los métodos de `Rabbit`, como `rabbit.hide()`, y también a los métodos `Animal`, como `rabbit.run()`. -Internally, `extends` keyword adds `[[Prototype]]` reference from `Rabbit.prototype` to `Animal.prototype`: +Internamente, la palabra clave `extends` funciona con la buena mecánica de prototipo. Establece `Rabbit.prototype. [[Prototype]]` a `Animal.prototype`. Entonces, si no se encuentra un método en `Rabbit.prototype`, JavaScript lo toma de` Animal.prototype`. ![](animal-rabbit-extends.svg) -So, if a method is not found in `Rabbit.prototype`, JavaScript takes it from `Animal.prototype`. +Por ejemplo, para encontrar el método `rabbit.run`, el motor verifica (de abajo hacia arriba en la imagen) que: +1. El objeto `rabbit` (no tiene el método `run`). +2. Su prototipo, que es `Rabbit.prototype` (tiene el método `hide`, pero no el método `run`). +3. Su prototipo, es decir (debido a `extends`) `Animal.prototype`, este finalmente tiene el método `run`. -As we can recall from the chapter , JavaScript uses the same prototypal inheritance for build-in objects. E.g. `Date.prototype.[[Prototype]]` is `Object.prototype`, so dates have generic object methods. +Como podemos recordar del capítulo , JavaScript usa la misma herencia prototípica para los objetos incorporados. P.ej. `Date.prototype.[[Prototype]]` es `Object.prototype`, por lo que "Date" tiene métodos de objeto genéricos. -````smart header="Any expression is allowed after `extends`" -Class syntax allows to specify not just a class, but any expression after `extends`. +````smart header="Cualquier expresión está permitida después de `extends`" +La sintaxis de clase permite especificar no solo una clase, sino cualquier expresión después de `extends`. -For instance, a function call that generates the parent class: +Por ejemplo, una llamada a función que genera la clase padre: ```js run function f(phrase) { @@ -107,39 +81,39 @@ function f(phrase) { } *!* -class User extends f("Hello") {} +class User extends f("Hola") {} */!* -new User().sayHi(); // Hello +new User().sayHi(); // Hola ``` -Here `class User` inherits from the result of `f("Hello")`. +Aquí `class User` hereda del resultado de `f("Hola")`. -That may be useful for advanced programming patterns when we use functions to generate classes depending on many conditions and can inherit from them. +Eso puede ser útil para patrones de programación avanzados cuando usamos funciones para generar clases dependiendo de muchas condiciones y podamos heredar de ellas. ```` -## Overriding a method +## Anular un método -Now let's move forward and override a method. As of now, `Rabbit` inherits the `stop` method that sets `this.speed = 0` from `Animal`. +Ahora avancemos y anulemos un método. Por defecto, todos los métodos que no están especificados en la clase `Rabbit` se toman directamente "tal cual" de la clase `Animal`. -If we specify our own `stop` in `Rabbit`, then it will be used instead: +Si especificamos nuestro propio método `stop()` en `Rabbit`, se utilizará en su lugar: ```js class Rabbit extends Animal { stop() { - // ...this will be used for rabbit.stop() + // ...esto se usará para rabbit.stop() + // en lugar de stop () de la clase Animal } } ``` +...Pero, por lo general, no queremos reemplazar totalmente un método padre, sino más bien construir sobre él, modificar o ampliar su funcionalidad. Hacemos algo en nuestro método, pero llamamos al método padre antes/después o en el proceso. -...But usually we don't want to totally replace a parent method, but rather to build on top of it, tweak or extend its functionality. We do something in our method, but call the parent method before/after it or in the process. +Las clases proporcionan la palabra clave `"super"` para eso. -Classes provide `"super"` keyword for that. +- `super.method(...)` llamar a un método padre. +- `super(...)` llamar a un constructor padre (solo dentro de nuestro constructor). -- `super.method(...)` to call a parent method. -- `super(...)` to call a parent constructor (inside our constructor only). - -For instance, let our rabbit autohide when stopped: +Por ejemplo, dejemos que nuestro conejo se oculte automáticamente cuando se detenga: ```js run class Animal { @@ -150,70 +124,70 @@ class Animal { } run(speed) { - this.speed += speed; - alert(`${this.name} runs with speed ${this.speed}.`); + this.speed = speed; + alert(`${this.name} corre a una velocidad de ${this.speed}.`); } stop() { this.speed = 0; - alert(`${this.name} stopped.`); + alert(`${this.name} se queda quieto.`); } } class Rabbit extends Animal { hide() { - alert(`${this.name} hides!`); + alert(`${this.name} se esconde!`); } *!* stop() { - super.stop(); // call parent stop - this.hide(); // and then hide + super.stop(); // llama el stop padre + this.hide(); // y luego hide } */!* } -let rabbit = new Rabbit("White Rabbit"); +let rabbit = new Rabbit("Conejo Blanco"); -rabbit.run(5); // White Rabbit runs with speed 5. -rabbit.stop(); // White Rabbit stopped. White rabbit hides! +rabbit.run(5); // Conejo Blanco corre a una velocidad de 5. +rabbit.stop(); // Conejo Blanco se queda quieto. Conejo Blanco se esconde! ``` -Now `Rabbit` has the `stop` method that calls the parent `super.stop()` in the process. +Ahora `Rabbit` tiene el método `stop` que llama al padre `super.stop()` en el proceso. -````smart header="Arrow functions have no `super`" -As was mentioned in the chapter , arrow functions do not have `super`. +````smart header="Las funciones de flecha no tienen `super`" +Como se mencionó en el capítulo , las funciones de flecha no tienen `super`. -If accessed, it's taken from the outer function. For instance: +Si se accede, se toma de la función externa. Por ejemplo: ```js class Rabbit extends Animal { stop() { - setTimeout(() => super.stop(), 1000); // call parent stop after 1sec + setTimeout(() => super.stop(), 1000); // llama al padre stop despues de 1seg } } ``` -The `super` in the arrow function is the same as in `stop()`, so it works as intended. If we specified a "regular" function here, there would be an error: +El método `super` en la función de flecha es el mismo que en `stop()`, por lo que funciona según lo previsto. Si especificamos una función "regular" aquí, habría un error: ```js -// Unexpected super +// super inesperado setTimeout(function() { super.stop() }, 1000); ``` ```` -## Overriding constructor +## Anular un constructor -With constructors it gets a little bit tricky. +Con los constructores se pone un poco complicado. -Till now, `Rabbit` did not have its own `constructor`. +Hasta ahora, `Rabbit` no tenía su propio `constructor`. -According to the [specification](https://tc39.github.io/ecma262/#sec-runtime-semantics-classdefinitionevaluation), if a class extends another class and has no `constructor`, then the following `constructor` is generated: +De acuerdo con la [especificación] (https://tc39.github.io/ecma262/#sec-runtime-semantics-classdefinitionevaluation), si una clase extiende otra clase y no tiene `constructor`, se genera el siguiente `constructor` "vacío": ```js class Rabbit extends Animal { - // generated for extending classes without own constructors + // generado para extender clases sin constructores propios *!* constructor(...args) { super(...args); @@ -222,9 +196,9 @@ class Rabbit extends Animal { } ``` -As we can see, it basically calls the parent `constructor` passing it all the arguments. That happens if we don't write a constructor of our own. +Como podemos ver, básicamente llama al padre `constructor` pasándole todos los argumentos. Eso sucede si no escribimos un constructor propio. -Now let's add a custom constructor to `Rabbit`. It will specify the `earLength` in addition to `name`: +Ahora agreguemos un constructor personalizado a `Rabbit`. Especificará `earLength` además de `name`: ```js run class Animal { @@ -249,29 +223,29 @@ class Rabbit extends Animal { } *!* -// Doesn't work! -let rabbit = new Rabbit("White Rabbit", 10); // Error: this is not defined. +// No funciona! +let rabbit = new Rabbit("Conejo Blanco", 10); // Error: esto no está definido. */!* ``` -Whoops! We've got an error. Now we can't create rabbits. What went wrong? +Whoops! Tenemos un error. Ahora no podemos crear conejos. ¿Qué salió mal? -The short answer is: constructors in inheriting classes must call `super(...)`, and (!) do it before using `this`. +La respuesta corta es: los constructores en las clases heredadas deben llamar a `super(...)`, y (!) Hacerlo antes de usar `this`. -...But why? What's going on here? Indeed, the requirement seems strange. +...¿Pero por qué? ¿Que está pasando aqui? De hecho, el requisito parece extraño. -Of course, there's an explanation. Let's get into details, so you'd really understand what's going on. +Por supuesto, hay una explicación. Vamos a entrar en detalles, para que realmente entiendas lo que está pasando. -In JavaScript, there's a distinction between a "constructor function of an inheriting class" and all others. In an inheriting class, the corresponding constructor function is labelled with a special internal property `[[ConstructorKind]]:"derived"`. +En JavaScript, hay una distinción entre una función constructora de una clase heredera (llamada "constructor derivado") y otras funciones. Un constructor derivado tiene una propiedad interna especial `[[ConstructorKind]]:"derived"`. Esa es una etiqueta interna especial. -The difference is: +Esa etiqueta afecta su comportamiento con `new`. -- When a normal constructor runs, it creates an empty object as `this` and continues with it. -- But when a derived constructor runs, it doesn't do it. It expects the parent constructor to do this job. +- Cuando una función regular se ejecuta con `new`, crea un objeto vacío y lo asigna a `this`. +- Pero cuando se ejecuta un constructor derivado, no hace esto. Espera que el constructor padre haga este trabajo. -So if we're making a constructor of our own, then we must call `super`, because otherwise the object with `this` reference to it won't be created. And we'll get an error. +Por lo tanto, un constructor derivado debe llamar a `super` para ejecutar su constructor padre (no derivado), de lo contrario no se creará el objeto para `this`. Y obtendremos un error. -For `Rabbit` to work, we need to call `super()` before using `this`, like here: +Para que el constructor `Rabbit` funcione, necesita llamar a `super()` antes de usar `this`, como aquí: ```js run class Animal { @@ -298,32 +272,40 @@ class Rabbit extends Animal { *!* // now fine -let rabbit = new Rabbit("White Rabbit", 10); -alert(rabbit.name); // White Rabbit +let rabbit = new Rabbit("Conejo Blanco", 10); +alert(rabbit.name); // Conejo Blanco alert(rabbit.earLength); // 10 */!* ``` -## Super: internals, [[HomeObject]] +## [[HomeObject]]: el `super` interno + +```warn header="Información avanzada" +Si está leyendo el tutorial por primera vez, esta sección puede omitirse. + +Esta sección trata de los mecanismos internos detrás de la herencia y el método `super`. +``` -Let's get a little deeper under the hood of `super`. We'll see some interesting things by the way. +Vamos a profundizar un poco más bajo la sombra de `super`. Veremos algunas cosas interesantes en el camino. -First to say, from all that we've learned till now, it's impossible for `super` to work. +En primer lugar, de todo lo que hemos aprendido hasta ahora, ¡es imposible que `super` funcione en absoluto! -Yeah, indeed, let's ask ourselves, how it could technically work? When an object method runs, it gets the current object as `this`. If we call `super.method()` then, how to retrieve the `method`? Naturally, we need to take the `method` from the prototype of the current object. How, technically, we (or a JavaScript engine) can do it? +Sí, de hecho, preguntémonos, ¿cómo debería funcionar técnicamente? Cuando se ejecuta un método de objeto, obtiene el objeto actual como `this`. Si llamamos a `super.method()` entonces, el motor necesita obtener el `method` del prototipo del objeto actual. ¿Pero cómo? -Maybe we can get the method from `[[Prototype]]` of `this`, as `this.__proto__.method`? Unfortunately, that doesn't work. +La tarea puede parecer simple, pero no lo es. El motor conoce el objeto actual `this`, por lo que podría obtener el `method` padre como `this.__proto __.method`. Desafortunadamente, una solución tan "ingenua" no funcionará. -Let's try to do it. Without classes, using plain objects for the sake of simplicity. +Demostremos el problema. Sin clases, usando objetos simples en aras de la simplicidad. -Here, `rabbit.eat()` should call `animal.eat()` method of the parent object: +Puedes omitir esta parte e ir a la subsección `[[HomeObject]]` si no deseas conocer los detalles. Eso no hará daño. O sigue leyendo si estás interesado en comprender las cosas en profundidad. + +En el siguiente ejemplo, `rabbit.__ proto__ = animal`. Ahora intentemos: en `rabbit.eat()` llamaremos a `animal.eat()`, usando `this.__ proto__`: ```js run let animal = { name: "Animal", eat() { - alert(`${this.name} eats.`); + alert(`${this.name} come.`); } }; @@ -332,33 +314,33 @@ let rabbit = { name: "Rabbit", eat() { *!* - // that's how super.eat() could presumably work + // así es como supuestamente podría funcionar super.eat() this.__proto__.eat.call(this); // (*) */!* } }; -rabbit.eat(); // Rabbit eats. +rabbit.eat(); // Rabbit come. ``` -At the line `(*)` we take `eat` from the prototype (`animal`) and call it in the context of the current object. Please note that `.call(this)` is important here, because a simple `this.__proto__.eat()` would execute parent `eat` in the context of the prototype, not the current object. +En la línea `(*)` tomamos `eat` del prototipo (`animal`) y lo llamamos en el contexto del objeto actual. Tenga en cuenta que `.call(this)` es importante aquí, porque un simple `this.__ proto __.eat()` ejecutaría al padre `eat` en el contexto del prototipo, no del objeto actual. -And in the code above it actually works as intended: we have the correct `alert`. +Y en el código anterior, en realidad funciona según lo previsto: tenemos el `alert` correcto. -Now let's add one more object to the chain. We'll see how things break: +Ahora agreguemos un objeto más a la cadena. Veremos cómo se rompen las cosas: ```js run let animal = { name: "Animal", eat() { - alert(`${this.name} eats.`); + alert(`${this.name} come.`); } }; let rabbit = { __proto__: animal, eat() { - // ...bounce around rabbit-style and call parent (animal) method + // ...rebota alrededor al estilo de conejo y llama al método padre (animal) this.__proto__.eat.call(this); // (*) } }; @@ -366,100 +348,158 @@ let rabbit = { let longEar = { __proto__: rabbit, eat() { - // ...do something with long ears and call parent (rabbit) method + // ...haz algo con orejas largas y llama al método padre (rabbit) this.__proto__.eat.call(this); // (**) } }; *!* -longEar.eat(); // Error: Maximum call stack size exceeded +longEar.eat(); // Error: Se excedió el número máximo de llamadas a la pila */!* ``` -The code doesn't work anymore! We can see the error trying to call `longEar.eat()`. +¡El código ya no funciona! Podemos ver el error al intentar llamar a `longEar.eat()`. -It may be not that obvious, but if we trace `longEar.eat()` call, then we can see why. In both lines `(*)` and `(**)` the value of `this` is the current object (`longEar`). That's essential: all object methods get the current object as `this`, not a prototype or something. +Puede que no sea tan obvio, pero si rastreamos la llamada `longEar.eat()`, entonces podemos ver por qué. En ambas líneas `(*)` y `(**)` el valor de `this` es el objeto actual (`longEar`). Eso es esencial: todos los métodos de objeto obtienen el objeto actual como `this`, no un prototipo o algo así. -So, in both lines `(*)` and `(**)` the value of `this.__proto__` is exactly the same: `rabbit`. They both call `rabbit.eat` without going up the chain in the endless loop. +Entonces, en ambas líneas `(*)` y `(**)` el valor de `this.__ proto__` es exactamente el mismo: `rabbit`. Ambos llaman a `rabbit.eat` sin subir la cadena en el bucle sin fin. -Here's the picture of what happens: +Aquí está la imagen de lo que sucede: ![](this-super-loop.svg) -1. Inside `longEar.eat()`, the line `(**)` calls `rabbit.eat` providing it with `this=longEar`. +1. Dentro de `longEar.eat()`, la línea `(**)` llama a `rabbit.eat` proporcionándole `this=longEar`. ```js - // inside longEar.eat() we have this = longEar + // dentro de longEar.eat() tenemos this = longEar this.__proto__.eat.call(this) // (**) - // becomes + // se convierte en longEar.__proto__.eat.call(this) - // that is + // es decir rabbit.eat.call(this); ``` -2. Then in the line `(*)` of `rabbit.eat`, we'd like to pass the call even higher in the chain, but `this=longEar`, so `this.__proto__.eat` is again `rabbit.eat`! +2. Luego, en la línea `(*)` de `rabbit.eat`, nos gustaría pasar la llamada aún más arriba en la cadena, pero `this=longEar`, entonces `this.__ proto __.eat` es nuevamente `rabbit.eat`! ```js - // inside rabbit.eat() we also have this = longEar + // dentro de rabbit.eat () también tenemos this = longEar this.__proto__.eat.call(this) // (*) - // becomes + // se convierte en longEar.__proto__.eat.call(this) - // or (again) + // o (de nuevo) rabbit.eat.call(this); ``` -3. ...So `rabbit.eat` calls itself in the endless loop, because it can't ascend any further. +3. ...Entonces `rabbit.eat` se llama a sí mismo en el bucle sin fin, porque no puede ascender más. -The problem can't be solved by using `this` alone. +El problema no se puede resolver usando solo `this`. ### `[[HomeObject]]` -To provide the solution, JavaScript adds one more special internal property for functions: `[[HomeObject]]`. - -**When a function is specified as a class or object method, its `[[HomeObject]]` property becomes that object.** +Para proporcionar la solución, JavaScript agrega una propiedad interna especial más para las funciones: `[[HomeObject]]`. -This actually violates the idea of "unbound" functions, because methods remember their objects. And `[[HomeObject]]` can't be changed, so this bound is forever. So that's a very important change in the language. +Cuando una función se especifica como un método de clase u objeto, su propiedad `[[HomeObject]]` se convierte en ese objeto. -But this change is safe. `[[HomeObject]]` is used only for calling parent methods in `super`, to resolve the prototype. So it doesn't break compatibility. +Entonces `super` lo usa para resolver el problema del prototipo padre y sus métodos. -Let's see how it works for `super` -- again, using plain objects: +Veamos cómo funciona, primero con objetos simples: ```js run let animal = { name: "Animal", - eat() { // [[HomeObject]] == animal - alert(`${this.name} eats.`); + eat() { // animal.eat.[[HomeObject]] == animal + alert(`${this.name} come.`); } }; let rabbit = { __proto__: animal, name: "Rabbit", - eat() { // [[HomeObject]] == rabbit + eat() { // rabbit.eat.[[HomeObject]] == rabbit super.eat(); } }; let longEar = { __proto__: rabbit, - name: "Long Ear", - eat() { // [[HomeObject]] == longEar + name: "Oreja Larga", + eat() { // longEar.eat.[[HomeObject]] == longEar super.eat(); } }; *!* -longEar.eat(); // Long Ear eats. +// funciona correctamente +longEar.eat(); // Oreja Larga come. */!* ``` -Every method remembers its object in the internal `[[HomeObject]]` property. Then `super` uses it to resolve the parent prototype. +Funciona según lo previsto, debido a la mecánica de `[[HomeObject]]`. Un método, como `longEar.eat`, conoce su `[[HomeObject]]` y toma el método padre de su prototipo. Sin el uso de `this`. + +### Los métodos no son "libres" + +Como hemos sabido antes, generalmente las funciones son "libres", no vinculadas a objetos en JavaScript. Para que puedan copiarse entre objetos y llamarse con otro 'this`. + +La existencia misma de `[[HomeObject]]` viola ese principio, porque los métodos recuerdan sus objetos. `[[HomeObject]]` no se puede cambiar, por lo que este vínculo es para siempre. -`[[HomeObject]]` is defined for methods defined both in classes and in plain objects. But for objects, methods must be specified exactly the given way: as `method()`, not as `"method: function()"`. +El único lugar en el lenguaje donde se usa `[[HomeObject]]` es en `super`. Si un método no usa `super`, entonces todavía podemos considerarlo "libre" y copiarlo entre objetos. Pero con `super` las cosas pueden salir mal. -In the example below a non-method syntax is used for comparison. `[[HomeObject]]` property is not set and the inheritance doesn't work: +Aquí está la demostración de un resultado `super` incorrecto después de copiar: ```js run let animal = { - eat: function() { // should be the short syntax: eat() {...} + sayHi() { + console.log(`Soy un animal`); + } +}; + +// conejo hereda de animal +let rabbit = { + __proto__: animal, + sayHi() { + super.sayHi(); + } +}; + +let plant = { + sayHi() { + console.log("Soy una planta"); + } +}; + +// arbol hereda de planta +let tree = { + __proto__: plant, +*!* + sayHi: rabbit.sayHi // (*) +*/!* +}; + +*!* +tree.sayHi(); // Soy un animal (?!?) +*/!* +``` + +Una llamada a `tree.sayHi()` muestra "Soy un animal". Definitivamente mal. + +La razón es simple: +- En la línea `(*)`, el método `tree.sayHi` se copió de `rabbit`. ¿Quizás solo queríamos evitar la duplicación de código? +- Su `[[HomeObject]]` es `rabbit`, ya que fue creado en `rabbit`. No hay forma de cambiar `[[HomeObject]]`. +- El código de `tree.sayHi()` tiene dentro a `super.sayHi()`. Sube de 'rabbit' y toma el método de 'animal'. + +Aquí está el diagrama de lo que sucede: + +![](super-homeobject-wrong.svg) + +### Métodos, no propiedades de función + +`[[HomeObject]]` se define para métodos tanto en clases como en objetos simples. Pero para los objetos, los métodos deben especificarse exactamente como `method()`, no como `"method: function()"`. + +La diferencia puede no ser esencial para nosotros, pero es importante para JavaScript. + +En el siguiente ejemplo, se utiliza una sintaxis que no es de método para la comparación. La propiedad `[[HomeObject]]` no está establecida y la herencia no funciona: + +```js run +let animal = { + eat: function() { // escribiendo intencionalmente así en lugar de eat() {... // ... } }; @@ -472,6 +512,21 @@ let rabbit = { }; *!* -rabbit.eat(); // Error calling super (because there's no [[HomeObject]]) +rabbit.eat(); // Error al llamar a super (porque no hay [[HomeObject]]) */!* ``` + +## Resumen + +1. Para extender una clase: `class Child extends Parent`: +     - Eso significa que `Child.prototype.__proto__` será `Parent.prototype`, por lo que los métodos se heredan. +2. Al anular un constructor: +     - Debemos llamar al constructor padre como `super()` en el constructor `Child` antes de usar `this`. +3. Al anular otro método: +     - Podemos usar `super.method()` en un método `Child` para llamar al método `Parent`. +4. Partes internas: +     - Los métodos recuerdan su clase/objeto en la propiedad interna `[[HomeObject]]`. Así es como `super` resuelve los métodos padres. +     - Por lo tanto, no es seguro copiar un método con `super` de un objeto a otro. + +También: +- Las funciones de flecha no tienen su propio `this` o` super`, por lo que se ajustan de manera transparente al contexto circundante. diff --git a/1-js/09-classes/02-class-inheritance/class-inheritance-array-object.svg b/1-js/09-classes/02-class-inheritance/class-inheritance-array-object.svg index 6f2124ebb..10af6c4c2 100644 --- a/1-js/09-classes/02-class-inheritance/class-inheritance-array-object.svg +++ b/1-js/09-classes/02-class-inheritance/class-inheritance-array-object.svg @@ -1,41 +1 @@ - - - - class-inheritance-array-object.svg - Created with sketchtool. - - - - - slice: function - ... - - - Array.prototype - - - arr - - - - hasOwnProperty: function - ... - - - Object.prototype - - - - - - [1, 2, 3] - - - [[Prototype]] - - - [[Prototype]] - - - - \ No newline at end of file +slice: function ...Array.prototypearrhasOwnProperty: function ...Object.prototype[1, 2, 3][[Prototype]][[Prototype]] \ No newline at end of file diff --git a/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal-2.svg b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal-2.svg index 9714d6708..a81676e25 100644 --- a/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal-2.svg +++ b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal-2.svg @@ -1,62 +1 @@ - - - - class-inheritance-rabbit-animal-2.svg - Created with sketchtool. - - - - - jump: function - - - Rabbit.prototype - - - rabbit - - - - eat: function - - - Animal.prototype - - - - - - name: "White Rabbit" - - - [[Prototype]] - - - [[Prototype]] - - - Rabbit.prototype.__proto__ = Animal.prototype sets this - - - - toString: function - hasOwnProperty: function - ... - - - Object.prototype - - - - [[Prototype]] - - - - [[Prototype]] - - - null - - - - \ No newline at end of file +jump: functionRabbit.prototyperabbiteat: functionAnimal.prototypename: "White Rabbit"[[Prototype]][[Prototype]]Rabbit.prototype.__proto__ = Animal.prototype sets thistoString: function hasOwnProperty: function ...Object.prototype[[Prototype]][[Prototype]]null \ No newline at end of file diff --git a/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal.svg b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal.svg index 249bfa4c9..35529aa43 100644 --- a/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal.svg +++ b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal.svg @@ -1,39 +1 @@ - - - - class-inheritance-rabbit-animal.svg - Created with sketchtool. - - - - - methods of Rabbit - - - Rabbit.prototype - - - rabbit - - - - methods of Animal - - - Animal.prototype - - - - - - [[Prototype]] - - - [[Prototype]] - - - properties of rabbit - - - - \ No newline at end of file +methods of RabbitRabbit.prototyperabbitmethods of AnimalAnimal.prototype[[Prototype]][[Prototype]]properties of rabbit \ No newline at end of file diff --git a/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-animal.svg b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-animal.svg index 7a16b7855..905efe37a 100644 --- a/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-animal.svg +++ b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-animal.svg @@ -1,42 +1 @@ - - - - rabbit-animal-independent-animal.svg - Created with sketchtool. - - - - - - constructor: Animal - run: function - stop: function - - - Animal.prototype - - - - Animal - - - - new Animal - - - - - [[Prototype]] - - - prototype - - - name: "My animal" - - - constructor - - - - \ No newline at end of file + constructor: Animal run: function stop: functionAnimal.prototypeAnimalnew Animal[[Prototype]]prototypename: "My animal" \ No newline at end of file diff --git a/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-rabbit.svg b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-rabbit.svg index ab936c151..81bf1850b 100644 --- a/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-rabbit.svg +++ b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-rabbit.svg @@ -1,41 +1 @@ - - - - rabbit-animal-independent-rabbit.svg - Created with sketchtool. - - - - - - constructor: Rabbit - hide: function - - - Rabbit.prototype - - - - Rabbit - - - - new Rabbit - - - - - [[Prototype]] - - - prototype - - - name: "My rabbit" - - - constructor - - - - \ No newline at end of file + constructor: Rabbit hide: functionRabbit.prototypeRabbitnew Rabbit[[Prototype]]prototypename: "My rabbit" \ No newline at end of file diff --git a/1-js/09-classes/02-class-inheritance/super-homeobject-wrong.svg b/1-js/09-classes/02-class-inheritance/super-homeobject-wrong.svg new file mode 100644 index 000000000..f13d441c9 --- /dev/null +++ b/1-js/09-classes/02-class-inheritance/super-homeobject-wrong.svg @@ -0,0 +1 @@ +sayHiplantsayHitreesayHianimalrabbit[[HomeObject]]sayHi \ No newline at end of file diff --git a/1-js/09-classes/02-class-inheritance/this-super-loop.svg b/1-js/09-classes/02-class-inheritance/this-super-loop.svg index 7862b41c0..bc200fab3 100644 --- a/1-js/09-classes/02-class-inheritance/this-super-loop.svg +++ b/1-js/09-classes/02-class-inheritance/this-super-loop.svg @@ -1,72 +1 @@ - - - - this-super-loop.svg - Created with sketchtool. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - rabbit - - - longEar - - - rabbit - - - longEar - - - - - - - - \ No newline at end of file +rabbitlongEarrabbitlongEar \ No newline at end of file diff --git a/1-js/09-classes/04-private-protected-properties-methods/article.md b/1-js/09-classes/04-private-protected-properties-methods/article.md index 7f891abab..82d67c943 100644 --- a/1-js/09-classes/04-private-protected-properties-methods/article.md +++ b/1-js/09-classes/04-private-protected-properties-methods/article.md @@ -1,102 +1,102 @@ -# Private and protected properties and methods +# Propiedades y métodos privados y protegidos. -One of the most important principles of object oriented programming -- delimiting internal interface from the external one. +Uno de los principios más importantes de la programación orientada a objetos: delimitar la interfaz interna de la externa. -That is "a must" practice in developing anything more complex than a "hello world" app. +Esa es una práctica "imprescindible" en el desarrollo de algo más complejo que una aplicación "hola mundo". -To understand this, let's break away from development and turn our eyes into the real world. +Para entender esto, alejémonos del desarrollo y volvamos nuestros ojos al mundo real.. -Usually, devices that we're using are quite complex. But delimiting the internal interface from the external one allows to use them without problems. +Por lo general, los dispositivos que estamos usando son bastante complejos. Pero delimitar la interfaz interna de la externa permite usarlas sin problemas. -## A real-life example +## Un ejemplo de la vida real -For instance, a coffee machine. Simple from outside: a button, a display, a few holes...And, surely, the result -- great coffee! :) +Por ejemplo, una máquina de café. Simple desde el exterior: un botón, una pantalla, algunos agujeros ... Y, seguramente, el resultado: ¡excelente café! :) ![](coffee.jpg) -But inside... (a picture from the repair manual) +Pero adentro ... (una imagen del manual de reparación) ![](coffee-inside.jpg) -A lot of details. But we can use it without knowing anything. +Muchos detalles. Pero podemos usarlo sin saber nada. -Coffee machines are quite reliable, aren't they? We can use one for years, and only if something goes wrong -- bring it for repairs. +Las cafeteras son bastante confiables, ¿no es así? Podemos usarlos por años, y solo si algo sale mal, tráigalo para repararlo. -The secret of reliability and simplicity of a coffee machine -- all details are well-tuned and *hidden* inside. +El secreto de la fiabilidad y la simplicidad de una máquina de café: todos los detalles están bien ajustados y *ocultos* en su interior. -If we remove the protective cover from the coffee machine, then using it will be much more complex (where to press?), and dangerous (it can electrocute). +Si retiramos la cubierta protectora de la cafetera, su uso será mucho más complejo (¿dónde presionar?) Y peligroso (puedes electrocutarte). -As we'll see, in programming objects are like coffee machines. +Como veremos, en la programación los objetos son como máquinas de café. -But in order to hide inner details, we'll use not a protective cover, but rather special syntax of the language and conventions. +Pero para ocultar detalles internos, no utilizaremos una cubierta protectora, sino una sintaxis especial del lenguaje y las convenciones. -## Internal and external interface +## Interfaz interna y externa -In object-oriented programming, properties and methods are split into two groups: +En la programación orientada a objetos, las propiedades y los métodos se dividen en dos grupos: -- *Internal interface* -- methods and properties, accessible from other methods of the class, but not from the outside. -- *External interface* -- methods and properties, accessible also from outside the class. +- *Interfaz interna* -- métodos y propiedades, accesibles desde otros métodos de la clase, pero no desde el exterior. +- *Interfaz externa* - métodos y propiedades, accesibles también desde fuera de la clase. -If we continue the analogy with the coffee machine -- what's hidden inside: a boiler tube, heating element, and so on -- is its internal interface. +Si continuamos la analogía con la máquina de café, lo que está oculto en su interior: un tubo de caldera, un elemento calefactor, etc., es su interfaz interna. -An internal interface is used for the object to work, its details use each other. For instance, a boiler tube is attached to the heating element. +Se utiliza una interfaz interna para que el objeto funcione, sus detalles se utilizan entre sí. Por ejemplo, un tubo de caldera está unido al elemento calefactor. -But from the outside a coffee machine is closed by the protective cover, so that no one can reach those. Details are hidden and inaccessible. We can use its features via the external interface. +Pero desde afuera, una máquina de café está cerrada por la cubierta protectora, para que nadie pueda alcanzarlos. Los detalles están ocultos e inaccesibles. Podemos usar sus funciones a través de la interfaz externa. -So, all we need to use an object is to know its external interface. We may be completely unaware how it works inside, and that's great. +Entonces, todo lo que necesitamos para usar un objeto es conocer su interfaz externa. Es posible que no seamos completamente conscientes de cómo funciona dentro, y eso es genial. -That was a general introduction. +Esa fue una introducción general. -In JavaScript, there are three types of properties and members: +En JavaScript, hay dos tipos de campos de objeto (propiedades y métodos): -- Public: accessible from anywhere. They comprise the external interface. Till now we were only using public properties and methods. -- Private: accessible only from inside the class. These are for the internal interface. +- Público: accesible desde cualquier lugar. Comprenden la interfaz externa. Hasta ahora solo estábamos usando propiedades y métodos públicos. +- Privado: accesible solo desde dentro de la clase. Estos son para la interfaz interna. -In many other languages there also exist "protected" fields: accessible only from inside the class and those extending it. They are also useful for the internal interface. They are in a sense more widespread than private ones, because we usually want inheriting classes to gain access to properly do the extension. +En muchos otros lenguajes también existen campos "protegidos": accesibles solo desde dentro de la clase y aquellos que lo extienden (como privado, pero más acceso desde clases heredadas). También son útiles para la interfaz interna. En cierto sentido, están más extendidos que los privados, porque generalmente queremos que las clases heredadas tengan acceso a ellas. -Protected fields are not implemented in Javascript on the language level, but in practice they are very convenient, so they are emulated. +Los campos protegidos no se implementan en JavaScript a nivel de lenguaje, pero en la práctica son muy convenientes, por lo que se emulan. -In the next step we'll make a coffee machine in Javascript with all these types of properties. A coffee machine has a lot of details, we won't model them to stay simple (though we could). +Ahora haremos una máquina de café en JavaScript con todos estos tipos de propiedades. Una máquina de café tiene muchos detalles, no los modelaremos todos, seremos simples (aunque podríamos). -## Protecting "waterAmount" +## Proteger "waterAmount" -Let's make a simple coffee machine class first: +Hagamos primero una clase de cafetera simple: ```js run class CoffeeMachine { - waterAmount = 0; // the amount of water inside + waterAmount = 0; // la cantidad de agua adentro constructor(power) { this.power = power; - alert( `Created a coffee-machine, power: ${power}` ); + alert( `Se creó una máquina de café, poder: ${power}` ); } } -// create the coffee machine +// se crea la máquina de café let coffeeMachine = new CoffeeMachine(100); -// add water +// agregar agua coffeeMachine.waterAmount = 200; ``` -Right now the properties `waterAmount` and `power` are public. We can easily get/set them from the outside to any value. +En este momento las propiedades `waterAmount` y` power` son públicas. Podemos obtenerlos/configurarlos fácilmente desde el exterior a cualquier valor. -Let's change `waterAmount` property to protected to have more control over it. For instance, we don't want anyone to set it below zero. +Cambiemos la propiedad `waterAmount` a protegida para tener más control sobre ella. Por ejemplo, no queremos que nadie lo ponga por debajo de cero. -**Protected properties are usually prefixed with an underscore `_`.** +**Las propiedades protegidas generalmente tienen el prefijo de subrayado `_`.** -That is not enforced on the language level, but there's a convention that such properties and methods should not be accessed from the outside. Most programmers follow it. +Eso no se aplica a nivel de lenguaje, pero existe una convención bien conocida entre los programadores de que no se debe acceder a tales propiedades y métodos desde el exterior. -So our property will be called `_waterAmount`: +Entonces nuestra propiedad se llamará `_waterAmount`: ```js run class CoffeeMachine { _waterAmount = 0; set waterAmount(value) { - if (value < 0) throw new Error("Negative water"); + if (value < 0) throw new Error("Agua en negativo"); this._waterAmount = value; } @@ -110,22 +110,22 @@ class CoffeeMachine { } -// create the coffee machine +// se crea la máquina de café let coffeeMachine = new CoffeeMachine(100); -// add water -coffeeMachine.waterAmount = -10; // Error: Negative water +// agregar agua +coffeeMachine.waterAmount = -10; // Error: Agua en negativo ``` -Now the access is under control, so setting the water below zero fails. +Ahora el acceso está bajo control, por lo que falla el ajuste del agua por debajo de cero. -## Read-only "power" +## "Power" de solo lectura -For `power` property, let's make it read-only. It sometimes happens that a property must be set at creation time only, and then never modified. +Para la propiedad `power`, hagámoslo de solo lectura. A veces sucede que una propiedad debe establecerse solo en el momento de la creación y nunca modificarse. -That's exactly the case for a coffee machine: power never changes. +Ese es exactamente el caso de una máquina de café: la potencia nunca cambia. -To do so, we only need to make getter, but not the setter: +Para hacerlo, solo necesitamos hacer `getter`, pero no `setter`: ```js run class CoffeeMachine { @@ -141,58 +141,58 @@ class CoffeeMachine { } -// create the coffee machine +// se crea la máquina de café let coffeeMachine = new CoffeeMachine(100); -alert(`Power is: ${coffeeMachine.power}W`); // Power is: 100W +alert(`La potencia es: ${coffeeMachine.power}W`); // Potencia es: 100W -coffeeMachine.power = 25; // Error (no setter) +coffeeMachine.power = 25; // Error (sin setter) ``` -````smart header="Getter/setter functions" -Here we used getter/setter syntax. +````smart header="Funciones getter/setter" +Aquí usamos la sintaxis getter/setter. -But most of the time `get.../set...` functions are preferred, like this: +Pero la mayoría de las veces las funciones `get.../set...` son preferidas, como esta: ```js class CoffeeMachine { _waterAmount = 0; *!*setWaterAmount(value)*/!* { - if (value < 0) throw new Error("Negative water"); + if (value < 0) throw new Error("Agua en negativo"); this._waterAmount = value; } *!*getWaterAmount()*/!* { - return this.waterAmount; + return this._waterAmount; } } new CoffeeMachine().setWaterAmount(100); ``` -That looks a bit longer, but functions are more flexible. They can accept multiple arguments (even if we don't need them right now). So, for the future, just in case we need to refactor something, functions are a safer choise. +Eso parece un poco más largo, pero las funciones son más flexibles. Pueden aceptar múltiples argumentos (incluso si no los necesitamos en este momento). -Surely, there's a tradeoff. On the other hand, get/set syntax is shorter, so ultimately there's no strict rule, it's up to you to decide. +Por otro lado, la sintaxis get/set es más corta, por lo que, en última instancia, no existe una regla estricta, depende de usted decidir. ```` -```smart header="Protected fields are inherited" -If we inherit `class MegaMachine extends CoffeeMachine`, then nothing prevents us from accessing `this._waterAmount` or `this._power` from the methods of the new class. +```smart header="Los campos protegidos son heredados." +Si heredamos `class MegaMachine extends CoffeeMachine`, entonces nada nos impide acceder a `this._waterAmount` o `this._power` desde los métodos de la nueva clase. -So protected fields are naturally inheritable. Unlike private ones that we'll see below. +Por lo tanto, los campos protegidos son naturalmente heredables. A diferencia de los privados que veremos a continuación. ``` -## Private "#waterLimit" +## "#waterLimit" Privada [recent browser=none] -There's a finished Javascript proposal, almost in the standard, that provides language-level support for private properties and methods. +Hay una propuesta de JavaScript terminada, casi en el estándar, que proporciona soporte a nivel de lenguaje para propiedades y métodos privados. -Privates should start with `#`. They are only accessible from inside the class. +Los privados deberían comenzar con `#`. Solo son accesibles desde dentro de la clase. -For instance, here we add a private `#waterLimit` property and extract the water-checking logic into a separate method: +Por ejemplo, aquí hay una propiedad privada `#waterLimit` y el método privado de control de agua `#checkWater`: -```js +```js run class CoffeeMachine { *!* #waterLimit = 200; @@ -200,41 +200,27 @@ class CoffeeMachine { *!* #checkWater(value) { - if (value < 0) throw new Error("Negative water"); - if (value > this.#waterLimit) throw new Error("Too much water"); + if (value < 0) throw new Error("Agua en negativo"); + if (value > this.#waterLimit) throw new Error("Demasiada agua"); } */!* - _waterAmount = 0; - - set waterAmount(value) { -*!* - this.#checkWater(value); -*/!* - this._waterAmount = value; - } - - get waterAmount() { - return this.waterAmount; - } - } let coffeeMachine = new CoffeeMachine(); *!* +// no puede acceder a privados desde fuera de la clase coffeeMachine.#checkWater(); // Error coffeeMachine.#waterLimit = 1000; // Error */!* - -coffeeMachine.waterAmount = 100; // Works ``` -On the language level, `#` is a special sign that the field is private. We can't access it from outside or from inheriting classes. +A nivel de lenguaje, `#` es una señal especial de que el campo es privado. No podemos acceder desde fuera o desde clases heredadas. -Private fields do not conflict with public ones. We can have both private `#waterAmount` and public `waterAmount` fields at the same time. +Los campos privados no entran en conflicto con los públicos. Podemos tener campos privados `#waterAmount` y públicos ` waterAmount` al mismo tiempo. -For instance, let's make `waterAmount` an accessor for `#waterAmount`: +Por ejemplo, hagamos que `waterAmount` sea un accesorio para `#waterAmount`: ```js run class CoffeeMachine { @@ -246,7 +232,7 @@ class CoffeeMachine { } set waterAmount(value) { - if (value < 0) throw new Error("Negative water"); + if (value < 0) throw new Error("Agua en negativo"); this.#waterAmount = value; } } @@ -257,74 +243,73 @@ machine.waterAmount = 100; alert(machine.#waterAmount); // Error ``` -Unlike protected ones, private fields are enforced by the language itself. That's a good thing. +A diferencia de los protegidos, los campos privados son aplicados por el propio lenguaje. Eso es bueno. -But if we inherit from `CoffeeMachine`, then we'll have no direct access to `#waterAmount`. We'll need to rely on `waterAmount` getter/setter: +Pero si heredamos de `CoffeeMachine`, entonces no tendremos acceso directo a `#waterAmount`. Tendremos que confiar en el getter/setter de `waterAmount`: ```js -class CoffeeMachine extends CoffeeMachine() { +class MegaCoffeeMachine extends CoffeeMachine { method() { *!* - alert( this.#waterAmount ); // Error: can only access from CoffeeMachine + alert( this.#waterAmount ); // Error: solo se puede acceder desde CoffeeMachine */!* } } ``` -In many scenarios such limitation is too severe. If we extend a `CoffeeMachine`, we may have legitimate reason to access its internals. That's why protected fields are used most of the time, even though they are not supported by the language syntax. +En muchos escenarios, esta limitación es demasiado severa. Si ampliamos una `CoffeeMachine`, es posible que tengamos razones legítimas para acceder a sus componentes internos. Es por eso que los campos protegidos se usan con más frecuencia, aunque no sean compatibles con la sintaxis del lenguaje. -````warn -Private fields are special. +````warn header="Los campos privados no están disponibles como this[name]" +Los campos privados son especiales. -Remember, usually we can access fields by this[name]: +Como sabemos, generalmente podemos acceder a los campos usando `this[name]`: ```js class User { ... sayHi() { - let fieldName = "name"; - alert(`Hello, ${this[fieldName]}`); + let fieldName = "nombre"; + alert(`Hello, ${*!*this[fieldName]*/!*}`); } } ``` -With private fields that's impossible: `this['#name']` doesn't work. That's a syntax limitation to ensure privacy. +Con campos privados eso es imposible: `this['#name']` no funciona. Esa es una limitación de sintaxis para garantizar la privacidad. ```` -## Summary +## Resumen -In terms of OOP, delimiting of the internal interface from the external one is called [encapsulation]("https://en.wikipedia.org/wiki/Encapsulation_(computer_programming)"). +En términos de POO, la delimitación de la interfaz interna de la externa se llama [encapsulamiento] (https://en.wikipedia.org/wiki/Encapsulation_ (computer_programming)). -It gives the following benefits: +Ofrece los siguientes beneficios: -Protection for users, so that they don't shoot themselves in the feet -: Imagine, there's a team of developers using a coffee machine. It was made by the "Best CoffeeMachine" company, and works fine, but a protective cover was removed. So the internal interface is exposed. +Protección para los usuarios, para que no se disparen en el pie: Imagínese, hay un equipo de desarrolladores que usan una máquina de café. Fue hecho por la compañía "Best CoffeeMachine" y funciona bien, pero se quitó una cubierta protectora. Entonces la interfaz interna está expuesta. - All developers are civilized -- they use the coffee machine as intended. But one of them, John, decided that he's the smartest one, and made some tweaks in the coffee machine internals. So the coffee machine failed two days later. + Todos los desarrolladores son civilizados: usan la máquina de café según lo previsto. Pero uno de ellos, John, decidió que él era el más inteligente e hizo algunos ajustes en el interior de la máquina de café. Entonces la máquina de café falló dos días después. - That's surely not John's fault, but rather the person who removed the protective cover and let John do his manipulations. + Seguramente no es culpa de John, sino de la persona que quitó la cubierta protectora y dejó que John hiciera sus manipulaciones. - The same in programming. If a user of a class will change things not intended to be changed from the outside -- the consequences are unpredictable. + Lo mismo en programación. Si un usuario de una clase cambiará cosas que no están destinadas a ser cambiadas desde el exterior, las consecuencias son impredecibles. -Supportable -: The situation in programming is more complex than with a real-life coffee machine, because we don't just buy it once. The code constantly undergoes development and improvement. +Soportable +: La situación en la programación es más compleja que con una máquina de café de la vida real, porque no solo la compramos una vez. El código se somete constantemente a desarrollo y mejora. - **If we strictly delimit the internal interface, then the developer of the class can freely change its internal properties and methods, even without informing the users..** + **Si delimitamos estrictamente la interfaz interna, el desarrollador de la clase puede cambiar libremente sus propiedades y métodos internos, incluso sin informar a los usuarios.** - It's much easier to develop, if you know that certain methods can be renamed, their parameters can be changed, and even removed, because no external code depends on them. + Si usted es un desarrollador de tal clase, es bueno saber que los métodos privados se pueden renombrar de forma segura, sus parámetros se pueden cambiar e incluso eliminar, porque ningún código externo depende de ellos. - For users, when a new version comes out, it may be a total overhaul, but still simple to upgrade if the external interface is the same. + Para los usuarios, cuando sale una nueva versión, puede ser una revisión total internamente, pero aún así es simple de actualizar si la interfaz externa es la misma. -Hiding complexity -: People adore to use things that are simple. At least from outside. What's inside is a different thing. +Ocultando complejidad +: La gente adora usar cosas que son simples. Al menos desde afuera. Lo que hay dentro es algo diferente. - Programmers are not an exception. + Los programadores no son una excepción. - **It's always convenient when implementation details are hidden, and a simple, well-documented external interface is available.** + **Siempre es conveniente cuando los detalles de implementación están ocultos, y hay disponible una interfaz externa simple y bien documentada.** -To hide internal interface we use either protected or public properties: +Para ocultar una interfaz interna utilizamos propiedades protegidas o privadas: -- Protected fields start with `_`. That's a well-known convention, not enforced at the language level. Programmers should only access a field starting with `_` from its class and classes inheriting from it. -- Private fields start with `#`. Javascript makes sure we only can access those from inside the class. +- Los campos protegidos comienzan con `_`. Esa es una convención bien conocida, no aplicada a nivel de lenguaje. Los programadores solo deben acceder a un campo que comience con `_` de su clase y las clases que hereden de él. +- Los campos privados comienzan con `#`. JavaScript se asegura de que solo podamos acceder a los que están dentro de la clase. -Right now, private fields are not well-supported among browsers, but can be polyfilled. +En este momento, los campos privados no son compatibles entre los navegadores, pero se pueden rellenar.