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..55553d454 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 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"); // ahora funciona */!* -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..124400a3d 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/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 deleted file mode 100644 index 34d783b4d..000000000 --- a/1-js/09-classes/02-class-inheritance/3-class-extend-object/rabbit-extends-object.svg +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index ca9e80601..000000000 --- a/1-js/09-classes/02-class-inheritance/3-class-extend-object/solution.md +++ /dev/null @@ -1,81 +0,0 @@ -First, let's see why the latter code doesn't work. - -The reason becomes obvious if we try to run it. An inheriting class constructor must call `super()`. Otherwise `"this"` won't be "defined". - -So here's the fix: - -```js run -class Rabbit extends Object { - constructor(name) { -*!* - super(); // need to call the parent constructor when inheriting -*/!* - this.name = name; - } -} - -let rabbit = new Rabbit("Rab"); - -alert( rabbit.hasOwnProperty('name') ); // true -``` - -But that's not all yet. - -Even after the fix, there's still important difference in `"class Rabbit extends Object"` versus `class Rabbit`. - -As we know, the "extends" syntax sets up two prototypes: - -1. Between `"prototype"` of the constructor functions (for methods). -2. Between the constructor functions themselves (for static methods). - -In our case, for `class Rabbit extends Object` it means: - -```js run -class Rabbit extends Object {} - -alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true -alert( Rabbit.__proto__ === Object ); // (2) true -``` - -So `Rabbit` now provides access to static methods of `Object` via `Rabbit`, like this: - -```js run -class Rabbit extends Object {} - -*!* -// normally we call 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`. - -Here's the demo: - -```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 - -*!* -// error, no such function in Rabbit -alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // Error -*/!* -``` - -So `Rabbit` doesn't provide access to static methods of `Object` in that case. - -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`. - -Here's the picture: - -![](rabbit-extends-object.svg) - -So, to put it short, there are two differences: - -| class Rabbit | class Rabbit extends Object | -|--------------|------------------------------| -| -- | needs to call `super()` in 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 deleted file mode 100644 index 1d0f98a74..000000000 --- a/1-js/09-classes/02-class-inheritance/3-class-extend-object/task.md +++ /dev/null @@ -1,42 +0,0 @@ -importance: 3 - ---- - -# Class extends Object? - -As we know, all objects normally inherit from `Object.prototype` and get access to "generic" object methods like `hasOwnProperty` etc. - -For instance: - -```js run -class Rabbit { - constructor(name) { - this.name = name; - } -} - -let rabbit = new Rabbit("Rab"); - -*!* -// hasOwnProperty method is from Object.prototype -alert( rabbit.hasOwnProperty('name') ); // true -*/!* -``` - -But if we spell it out explicitly like `"class Rabbit extends Object"`, then the result would be different from a simple `"class Rabbit"`? - -What's the difference? - -Here's an example of such code (it doesn't work -- why? fix it?): - -```js -class Rabbit extends Object { - constructor(name) { - this.name = name; - } -} - -let rabbit = new Rabbit("Rab"); - -alert( rabbit.hasOwnProperty('name') ); // Error -``` diff --git a/1-js/09-classes/02-class-inheritance/article.md b/1-js/09-classes/02-class-inheritance/article.md index 3d3c145eb..22b2e65c5 100644 --- a/1-js/09-classes/02-class-inheritance/article.md +++ b/1-js/09-classes/02-class-inheritance/article.md @@ -1,13 +1,13 @@ -# Class inheritance +# Herencia de clase -Class inheritance is a way for one class to extend another class. +La herencia de clase es un método para que una clase extienda a otra. -So we can create new functionality on top of the existing. +Entonces podemos crear una nueva funcionalidad además de la existente. -## The "extends" keyword +## La palabra clave "extends" -Let's say we have class `Animal`: +Digamos que tenemos la clase `Animal`: ```js class Animal { @@ -17,61 +17,61 @@ class Animal { } run(speed) { this.speed = speed; - alert(`${this.name} runs with speed ${this.speed}.`); + alert(`${this.name} corre a una velocidad de ${this.speed}.`); } stop() { this.speed = 0; - alert(`${this.name} stands still.`); + alert(`${this.name} se queda quieto.`); } } -let animal = new Animal("My animal"); +let animal = new Animal("Mi animal"); ``` -Here's how we can represent `animal` object and `Animal` class graphically: +Así es como podemos representar gráficamente el objeto `animal` y la clase `Animal`: ![](rabbit-animal-independent-animal.svg) -...And we would like to create another `class Rabbit`. +...Y nos gustaría crear otra clase `Rabbit`. -As rabbits are animals, `Rabbit` class should be based on `Animal`, have access to animal methods, so that rabbits can do what "generic" animals can do. +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. -The syntax to extend another class is: `class Child extends Parent`. +La sintaxis para extender otra clase es: `class Child extends Parent`. -Let's create `class Rabbit` that inherits from `Animal`: +Construyamos la clase `Rabbit` que herede de `Animal`: ```js *!* 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! ``` -Object of `Rabbit` class have access to both `Rabbit` methods, such as `rabbit.hide()`, and also to `Animal` methods, such as `rabbit.run()`. +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 works using the good old prototype mechanics. It sets `Rabbit.prototype.[[Prototype]]` to `Animal.prototype`. So, if a method is not found in `Rabbit.prototype`, JavaScript takes it from `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) -For instance, to find `rabbit.run` method, the engine checks (bottom-up on the picture): -1. The `rabbit` object (has no `run`). -2. Its prototype, that is `Rabbit.prototype` (has `hide`, but not `run`). -3. Its prototype, that is (due to `extends`) `Animal.prototype`, that finally has the `run` method. +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, `Animal.prototype` (debido a `extends`), este finalmente tiene el método `run`. -As we can recall from the chapter , JavaScript itself uses prototypal inheritance for built-in objects. E.g. `Date.prototype.[[Prototype]]` is `Object.prototype`. That's why dates have access to 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) { @@ -81,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")`. +Observa: `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 +## Sobrescribir un método -Now let's move forward and override a method. By default, all methods that are not specified in `class Rabbit` are taken directly "as is" from `class Animal`. +Ahora avancemos y sobrescribamos 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`. -But if we specify our own method in `Rabbit`, such as `stop()` 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() { - // ...now this will be used for rabbit.stop() - // instead of stop() from class Animal + // ...esto se usará para rabbit.stop() + // en lugar de stop() de la clase Animal } } ``` -Usually we don't want to totally replace a parent method, but rather to build on top of it to tweak or extend its functionality. We do something in our method, but call the parent method before/after it or in the process. +...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. -Classes provide `"super"` keyword for that. +Las clases proporcionan la palabra clave `"super"` para eso. -- `super.method(...)` to call a parent method. -- `super(...)` to call a parent constructor (inside our constructor only). +- `super.method(...)` llama un método padre. +- `super(...)` llama un constructor padre (solo dentro de nuestro constructor). -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 { @@ -125,69 +125,69 @@ class Animal { run(speed) { this.speed = speed; - alert(`${this.name} runs with speed ${this.speed}.`); + alert(`${this.name} corre a una velocidad de ${this.speed}.`); } stop() { this.speed = 0; - alert(`${this.name} stands still.`); + 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 stands still. 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 stop() padre 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()`, y funciona según lo previsto. Si aquí especificamos una función "regular", habría un error: ```js -// Unexpected super +// super inesperado setTimeout(function() { super.stop() }, 1000); ``` ```` -## Overriding constructor +## Sobrescribir un constructor -With constructors it gets a little bit tricky. +Con los constructores se pone un poco complicado. -Until 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 "empty" `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); @@ -196,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 { @@ -223,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? +Vaya! 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'll 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 (so-called "derived constructor") and other functions. A derived constructor has a special internal property `[[ConstructorKind]]:"derived"`. That's a special internal label. +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. -That label affects its behavior with `new`. +Esa etiqueta afecta su comportamiento con `new`. -- When a regular function is executed with `new`, it creates an empty object and assigns it to `this`. -- But when a derived constructor runs, it doesn't do this. 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 a derived constructor must call `super` in order to execute its parent (non-derived) constructor, otherwise the object for `this` 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 the `Rabbit` constructor to work, it needs 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 { @@ -272,75 +272,75 @@ 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="Advanced information" -If you're reading the tutorial for the first time - this section may be skipped. +```warn header="Información avanzada" +Si está leyendo el tutorial por primera vez, esta sección puede omitirse. -It's about the internal mechanisms behind inheritance and `super`. +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 along the way. +Vamos a profundizar un poco más el tema 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 at all! +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 should technically work? When an object method runs, it gets the current object as `this`. If we call `super.method()` then, the engine needs to get the `method` from the prototype of the current object. But how? +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? -The task may seem simple, but it isn't. The engine knows the current object `this`, so it could get the parent `method` as `this.__proto__.method`. Unfortunately, such a "naive" solution won'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 demonstrate the problem. Without classes, using plain objects for the sake of simplicity. +Demostremos el problema. Sin clases, usando objetos puros por simplicidad. -You may skip this part and go below to the `[[HomeObject]]` subsection if you don't want to know the details. That won't harm. Or read on if you're interested in understanding things in-depth. +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. -In the example below, `rabbit.__proto__ = animal`. Now let's try: in `rabbit.eat()` we'll call `animal.eat()`, using `this.__proto__`: +En el siguiente ejemplo, se hace la asignación `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.`); } }; let rabbit = { __proto__: animal, - name: "Rabbit", + name: "Conejo", 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(); // Conejo 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, 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 al estilo de conejo y llama al método padre (animal) this.__proto__.eat.call(this); // (*) } }; @@ -348,71 +348,71 @@ 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 depuramos la llamada `longEar.eat()`, podremos 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, evitando `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]]`. +Para proporcionar la solución, JavaScript agrega una propiedad interna especial para las funciones: `[[HomeObject]]`. -When a function is specified as a class or object method, its `[[HomeObject]]` property becomes that object. +Cuando una función se especifica como un método de clase u objeto, su propiedad `[[HomeObject]]` se convierte en ese objeto. -Then `super` uses it to resolve the parent prototype and its methods. +Entonces `super` lo usa para resolver el problema del prototipo padre y sus métodos. -Let's see how it works, first with plain objects: +Veamos cómo funciona, primero con objetos simples: ```js run let animal = { name: "Animal", eat() { // animal.eat.[[HomeObject]] == animal - alert(`${this.name} eats.`); + alert(`${this.name} come.`); } }; let rabbit = { __proto__: animal, - name: "Rabbit", + name: "Conejo", eat() { // rabbit.eat.[[HomeObject]] == rabbit super.eat(); } @@ -420,38 +420,38 @@ let rabbit = { let longEar = { __proto__: rabbit, - name: "Long Ear", + name: "Oreja Larga", eat() { // longEar.eat.[[HomeObject]] == longEar super.eat(); } }; *!* -// works correctly -longEar.eat(); // Long Ear eats. +// funciona correctamente +longEar.eat(); // Oreja Larga come. */!* ``` -It works as intended, due to `[[HomeObject]]` mechanics. A method, such as `longEar.eat`, knows its `[[HomeObject]]` and takes the parent method from its prototype. Without any use of `this`. +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`. -### Methods are not "free" +### Los métodos no son "libres" -As we've known before, generally functions are "free", not bound to objects in JavaScript. So they can be copied between objects and called with another `this`. +Como hemos sabido antes, generalmente las funciones son "libres", es decir no están vinculadas a objetos en JavaScript. Para que puedan copiarse entre objetos y llamarse con otro 'this`. -The very existence of `[[HomeObject]]` violates that principle, because methods remember their objects. `[[HomeObject]]` can't be changed, so this bond is forever. +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. -The only place in the language where `[[HomeObject]]` is used -- is `super`. So, if a method does not use `super`, then we can still consider it free and copy between objects. But with `super` things may go wrong. +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. -Here's the demo of a wrong `super` result after copying: +Aquí está la demostración de un resultado incorrecto de `super` después de copiarlo: ```js run let animal = { sayHi() { - console.log(`I'm an animal`); + console.log(`Soy un animal`); } }; -// rabbit inherits from animal +// rabbit hereda de animal let rabbit = { __proto__: animal, sayHi() { @@ -461,11 +461,11 @@ let rabbit = { let plant = { sayHi() { - console.log("I'm a plant"); + console.log("Soy una planta"); } }; -// tree inherits from plant +// tree hereda de plant let tree = { __proto__: plant, *!* @@ -474,32 +474,32 @@ let tree = { }; *!* -tree.sayHi(); // I'm an animal (?!?) +tree.sayHi(); // Soy un animal (?!?) */!* ``` -A call to `tree.sayHi()` shows "I'm an animal". Definitely wrong. +Una llamada a `tree.sayHi()` muestra "Soy un animal". Definitivamente mal. -The reason is simple: -- In the line `(*)`, the method `tree.sayHi` was copied from `rabbit`. Maybe we just wanted to avoid code duplication? -- Its `[[HomeObject]]` is `rabbit`, as it was created in `rabbit`. There's no way to change `[[HomeObject]]`. -- The code of `tree.sayHi()` has `super.sayHi()` inside. It goes up from `rabbit` and takes the method from `animal`. +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 desde 'rabbit' y toma el método de 'animal'. -Here's the diagram of what happens: +Aquí está el diagrama de lo que sucede: ![](super-homeobject-wrong.svg) -### Methods, not function properties +### Métodos, no propiedades de función -`[[HomeObject]]` is defined for methods both in classes and in plain objects. But for objects, methods must be specified exactly as `method()`, not as `"method: function()"`. +`[[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()"`. -The difference may be non-essential for us, but it's important for JavaScript. +La diferencia puede no ser esencial para nosotros, pero es importante para JavaScript. -In the example below a non-method syntax is used for comparison. `[[HomeObject]]` property is not set and the inheritance doesn't work: +En el siguiente ejemplo, se utiliza una sintaxis sin método para la comparación. La propiedad `[[HomeObject]]` no está establecida y la herencia no funciona: ```js run let animal = { - eat: function() { // intentionally writing like this instead of eat() {... + eat: function() { // escrito así intencionalmente en lugar de eat() {... // ... } }; @@ -512,21 +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]]) */!* ``` -## Summary +## Resumen -1. To extend a class: `class Child extends Parent`: - - That means `Child.prototype.__proto__` will be `Parent.prototype`, so methods are inherited. -2. When overriding a constructor: - - We must call parent constructor as `super()` in `Child` constructor before using `this`. -3. When overriding another method: - - We can use `super.method()` in a `Child` method to call `Parent` method. -4. Internals: - - Methods remember their class/object in the internal `[[HomeObject]]` property. That's how `super` resolves parent methods. - - So it's not safe to copy a method with `super` from one object to another. +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 sobrescribir un constructor: +     - Debemos llamar al constructor padre como `super()` en el constructor `Child` antes de usar `this`. +3. Al sobrescribir otro método: +     - Podemos usar `super.method()` en un método `Child` para llamar al método `Parent`. +4. Características 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. -Also: -- Arrow functions don't have their own `this` or `super`, so they transparently fit into the surrounding context. +También: +- Las funciones de flecha no tienen su propio `this` o `super`, por lo que se ajustan de manera transparente al contexto circundante.