Object Oriented Programming - Josej2r/JavaScript_Algorithms_and_Data_Structures_Certification GitHub Wiki

Object Oriented Programming

Introduction to the Object Oriented Programming Challenges

En esencia, el desarrollo de software resuelve un problema o logra un resultado con la computación. El proceso de desarrollo de software primero define un problema, luego presenta una solución. La programación orientada a objetos es no de varios enfoques importantes para el proceso de desarrollo de software.

Como su nombre lo indica, la programación orientada a objetos organiza el código en definiciones de objetos. A veces se denominan clases y agrupan datos como comportamientos relacionados. Los datos son los atributos de un objeto, y el comportamiento (o funciones) son métodos.

La estructura del objeto lo hace flexible dentro de un programa. Los objetos pueden transferir información llamando y pasando datos a los métodos de otro objeto. Además, las nuevas clases pueden recibir o heredar todas las características de una clase base o primaria. Esto ayuda a reducir el código repetido.

Su elección del enfoque de programación depende de algunos factores. Estos incluyen el tipo de problema, así como también cómo desea estructurar sus datos y algoritmos. Esta sección cubre los principios de programación orientada a objetos en JavaScript.

Upcoming Lessons

Create a Basic JavaScript Object

Piense en cosas que la gente ve todos los días, como automóviles, tiendas y pájaros. Todos estos son objetos: cosas tangibles con las que las personas pueden observar e interactuar.

¿Cuáles son algunas cualidades de esto objetos?. Un auto tiene ruedas. Las tiendas venden artículos. Las aves tienen alas.

Estas cualidades o propiedades definen lo que constituye un objeto. Tenga en cuenta que los objetos similares comparten las mismas propiedades, pero pueden tener valores diferentes para esas propiedades. Por ejemplo, todos los automóviles tienen ruedas, pero no todos tienen la misma cantidad de ruedas.

Los objetos en JavaScript se usan para modelar objetos del mundo real, dándoles propiedades y comportamiento al igual que sus contrapartes del mundo real. Aquí hay un ejemplo usando estos conceptos para crear un objeto de duck.

let duck = {
  name: "Aflac",
  numLegs: 2
};

Este objeto duck tiene dos pares de propiedad/valor un name de "Aflac" y un ```numLegs`` de 2.

Cree un objeto dog con las propiedades name y numLegs y configúrelos en una cadena y un número, respectivamente.

El código con la solución se encuentra aquí:Download

Use Dot Notation to Access the Properties of an Object

El último desafío creó un objeto con varias propiedades. Ahora verá cómo acceder a los valores de esas propiedades. Aquí hay un ejemplo:

let duck = {
  name: "Aflac",
  numLegs: 2
};
console.log(duck.name);
// This prints "Aflac" to the console

La notación de punto se usa en el nombre del objeto , duck, seguido del nombre de la propiedad, name, para acceder al valor de "Aflac".

Imprima ambas propiedades del objeto perro en su consola.

El código con la solución se encuentra aquí:Download

Create a Method on an Object

Los objetos pueden tener un tipo especial de propiedad, llamado método.

Los métodos son propiedades que son funciones. Esto agrega un comportamiento diferente a un objeto. Aquí está el ejemplo del pato con un método:

let duck = {
  name: "Aflac",
  numLegs: 2,
  sayName: function() {return "The name of this duck is " + duck.name + ".";}
};
duck.sayName();
// Returns "The name of this duck is Aflac."

El ejemplo agrega el método sayName, que es una función que devuelve una oración con el nombre del pato. Observe que el método accedió a la propiedad de name en la declaración de devolución usando duck.name. El próximo desafío cubrirá otra forma de hacer esto.

Usando el objeto de perro, dele un método llamado sayLegs. El método debe devolver la oración "This dog has 4 legs."

El código con la solución se encuentra aquí:Download

Make Code More Reusable with the this Keyword

El último desafío introdujo un método para el objeto duck. Usó la notación de punto duck.name para acceder al valor de la propiedad name dentro de la declaración de devolución:

sayName: function() {return "The name of this duck is " + duck.name + ".";}

Si bien esta es una forma válida acceder a la propiedad del objeto, aquí hay una trampa. Si el nombre de la variable cambia, cualquier código que haga referencia al nombre original también debería actualizarse. En una definición breve de objeto, no es un problema, pero si un objeto tiene muchas referencias a sus propiedades, hay una mayor posibilidad de error.

Una forma de evitar estos problemas es con la palabra clave this:

let duck = {
  name: "Aflac",
  numLegs: 2,
  sayName: function() {return "The name of this duck is " + this.name + ".";}
};

this es un tema profundo, y el ejemplo anterior es solo una forma de usarlo. En el contexto actual, esto se refiere al objeto con el que está asociado el método: duck. Si el bnombre del objeto se cambia a mallard, no es necesario encontrar todas las referencias a duck en el código. Hace que el código sea reutilizable y más fácil de leer.

Modifique el método dog.sayLegs para eliminar cualquier referencia a dog. Use el ejemplo de duck como guía.

El código con la solución se encuentra aquí:Download

Define a Constructor Function

Los constructores son funciones que crean nuevos objetos. Definen propiedades y comportamientos que pertenecerán al nuevo objeto. Piense en ellos como una plantilla para la creación de nuevos objetos.

Aquí hay un ejemplo de constructor:

function Bird() {
  this.name = "Albert";
  this.color = "blue";
  this.numLegs = 2;
}

Este constructor define un objeto Bird con propiedades name, color y numLegs establecidos en Albert, blue y 2 respectivamente. Los constructores siguen algunas convenciones:

  • Los constructores se definen con un nombre en mayúsculas para distinguirlos de otras funciones que no son constructores.

  • Los constructores usan la palabra this para establecer las propiedades del objeto que crearán. Dentro del constructor, esto se refiere al nuevo objeto que creará.

  • Los constructores definen propiedades y comportamiento en lugar de devolver un valor como podrían hacerlo otras funciones.

Cree un constructor, Dog con propiedades de name, color y numLegs que se configuran en una cadena, una cadena y número, respectivamente.

El código con la solución se encuentra aquí:Download

Use a Constructor to Create Objects

Aquí está el constructor Bird del desafío anterior:

function Bird() {
  this.name = "Albert";
  this.color  = "blue";
  this.numLegs = 2;
  // "this" inside the constructor always refers to the object being created
}

let blueBird = new Bird();

Observe que el operador new se usa al llamar a un constructor. Esto le dice a JavaScript que cree una nueva instancia de Bird llamada blueBird. Sin el operador new, this dentro del constructor no apuntaría al objeto recién creado, dando resultados inesperados. Ahora blueBird tiene todas las propiedades definidas dentro del constructor Bird:

blueBird.name; // => Albert
blueBird.color; // => blue
blueBird.numLegs; // => 2

Al igual que cualquier otro objeto, se puede acceder y modificar sus propiedades:

blueBird.name = 'Elvira';
blueBird.name; // => Elvira

Use el constructor Dog de la última lección para crear una nueva instacia de Dog, asignándola a una variable hound.

El código con la solución se encuentra aquí:Download

Extend Constructors to Receive Arguments

Los constructores de Bird y Dog del último desafío funcionan bien. Sin embargo, tenga en cuenta que todos los Birds creados con el constructor Bird se denominan automáticamente Albert, son de color azuñ y tienen dos patas. ¿Qué pasa si quieres pájaros con diferentes valores de nombres y colores? Es posible cambiar las propiedades de cada ave manualmente, pero eso sería mucho trabajo:

let swan = new Bird();
swan.name = "Carlos";
swan.color = "white";

Suponga que esta escribiendo un programa para realizar un seguimiento de cientos o incluso miles de aves diferentes en un aviario. Se necesitaría muchos tiempo para crear todas las aves, luego cambiar las propiedades a diferentes valores cada una. Para crear más fácilmente objetos Bird, puede diseñar su constructor Bird para aceptar parámetros:

function Bird(name, color) {
  this.name = name;
  this.color = color;
  this.numLegs = 2;
}

Luego, pase los valores como argumentos para definir cada ave única en el constructor: let cardinal = new Bird("Bruce", "red"); . Esto proporciona una nueva instancia de Bird con propiedades de name y color establecidas en Bruce y red respectivamente. La propiedad numLegs todavía está establecida en 2. El cardinal tiene estas propiedades.

cardinal.name // => Bruce
cardinal.color // => red
cardinal.numLegs // => 2

El constructor es más flexible. Ahora es posible definir las propiedades para cada Bird en el momento de su creación, que es una forma en que los constructores de JavaScript son tan útiles. Agrupan objetos en función de características y comportamientos compartidos y definen un plan que automatiza su creación.

Crea otro constructor de Dog. Esta vez, configúrelo para tomar el name y el color de los parámetros, y tenga la propiedad numLegs fijada en 4. Luego, cree un nuevo perro guardado como terrier. Pásalo dos cadenas argumentos para las propiedades de name y color.

El código con la solución se encuentra aquí:Download

Verify an Object's Constructor with instanceof

Cada vez que una función constructora crea un nuevo objeto, se dice que ese objeto es una instancia de su constructor. JavaScript proporciona una forma conveniente de verificar esto con el operador instanceof instanceof le permite comprobar un objeto con un constructor, devolviendo verdadero o falso en función de si ese objeto fue creado o no con el constructor. Aquí hay un ejemplo:

let Bird = function(name, color) {
  this.name = name;
  this.color = color;
  this.numLegs = 2;
}

let crow = new Bird("Alexis", "black");

crow instanceof Bird; // => true

Si se crea un objeto sin usar un constructor, instanceof verificará que no sea una instancia de ese constructor:

let canary = {
  name: "Mildred",
  color: "Yellow",
  numLegs: 2
};

canary instanceof Bird; // => false

Cree una nueva instancia del constructor House, llamándolo myHouse y pasando varios dormitorios. Luego use instanceof para verificar que es una instancia de House.

El código con la solución se encuentra aquí:Download

Understand Own Properties

En el siguiente ejemplo, el constructor Bird define dos propiedades: name y numLegs:

function Bird(name) {
  this.name  = name;
  this.numLegs = 2;
}

let duck = new Bird("Donald");
let canary = new Bird("Tweety");

name y numLegs se denominan propiedades propias, porque se definen directamente en el objeto de instancia. Eso significa que duck y canary tienen cada uno su propia copia separada de estas propiedades. De hecho, cada instancia de Bird tendrá su propia copia de estas propiedades. El siguiente código agrega todas las propiedades propias de duck a la matiz ownProps:

let ownProps = [];

for (let property in duck) {
  if(duck.hasOwnProperty(property)) {
    ownProps.push(property);
  }
}

console.log(ownProps); // prints [ "name", "numLegs" ]

Agregue las propiedades own de canary a la matriz ownProps.

El código con la solución se encuentra aquí:Download

Use Prototype Properties to Reduce Duplicate Code

Dado que numLegs probablemente tendrá el mismo valor para todas las instancias de Bird, esencialmente tiene una variable duplicada numLegs dentro de cada instancia de Bird.

Esto puede no ser un problema cuando solo hay dos instancias, pero imagine si hay millones de instancias. Eso sería una grna cantidad de variables duplicadas.

Una mejor manera es usar el prototype de Bird. Las propiedades en el prototype comparten entre TODAS las instancias de Bird. Aquí se explica cómo agregar numLegs al prototype de Bird:

Bird.prototype.numLegs = 2;

Ahora todas las instancias de Bird tienen la propiedad numLegs.

console.log(duck.numLegs);  // prints 2
console.log(canary.numLegs);  // prints 2

Como todas las instancias tienen automáticamente las propiedades en el prototipo, piense en un prototi`p como una "receta" para crear objetos. Tenga en cuenta que el prototipo para duck y canary es parte del constructor Bird como Bird.prototype. Casi todos los objetos en JavaScript tienen una propiedad ``prototype`` que es parte de la función constructora que la creó.

Agregue una propiedad numLegs al prototype de Dog.

El código con la solución se encuentra aquí:Download

Iterate Over All Properties

Ahora ha visto dos tipos de propiedades: own propiedades propias y las prototype. Own se definen directamente en la instancia del objeto en sí. Y las propiedades de prototype se definen en el prototype.

function Bird(name) {
  this.name = name;  //own property
}

Bird.prototype.numLegs = 2; // prototype property

let duck = new Bird("Donald");

Así es como se agregan las propiedades propias del duck a la matriz ownProps y las propiedades del prototype a la matriz prototypeProps.

let ownProps = [];
let prototypeProps = [];

for (let property in duck) {
  if(duck.hasOwnProperty(property)) {
    ownProps.push(property);
  } else {
    prototypeProps.push(property);
  }
}

console.log(ownProps); // prints ["name"]
console.log(prototypeProps); // prints ["numLegs"]

Agregue todas las propiedades propias de beagle a la matriz ownProps. Agregue todas las propiedades de prototype de Dog a la matriz prototypeProps.

El código con la solución se encuentra aquí:Download

Understand the Constructor Property

Hay una propiedad de constructor especial ubicada en las instancias del objeto duck y beagle que se crearon en los desafíos anteriores:

let duck = new Bird();
let beagle = new Dog();

console.log(duck.constructor === Bird);  //prints true
console.log(beagle.constructor === Dog);  //prints true

Tenga en cuenta que la propiedad constructor es una referencia a la función del constructor que creó la instancia. La ventaja de la propiedad del constructor es que es posible verificar esta propiedad para averiguar qué tipo de objeto es. Aquí hay un ejemplo de cómo se podría usar esto:

function joinBirdFraternity(candidate) {
  if (candidate.constructor === Bird) {
    return true;
  } else {
    return false;
  }
}

Nota: dado que la propiedad del constructor se puede sobrescribir (que se cubrirá en los próximos desafíos), generalmente es mejor usar el método de instanceof para verificar el tipo de un objeto.

Escriba una función joinDogFratenity que tome un parámetro candidate y , utilizando la propiedad del constructor, devuelva true si el candidate es un Dog, de lo contrario devuelva falso.

El código con la solución se encuentra aquí:Download

Change the Prototype to a New Object

Hasta ahora, ha estado agregando propiedades al prototype individualmente:

Bird.prototype.numLegs = 2;

Esto se vuelve tedioso después de más de unas pocas propiedades.

Bird.prototype.eat = function() {
  console.log("nom nom nom");
}

Bird.prototype.describe = function() {
  console.log("My name is " + this.name);
}

Una forma más eficiente es establecer el prototype en un nuevo objeto que ya contenga las propiedades. De esta manera, las propiedades se agregan todas a la vez:

Bird.prototype = {
  numLegs: 2, 
  eat: function() {
    console.log("nom nom nom");
  },
  describe: function() {
    console.log("My name is " + this.name);
  }
};

Agregue la propiedad numLegs y los métodos eat() y describe() al prototype de Dog configurando el prototype a un nuevo objeto.

El código con la solución se encuentra aquí:Download

Remember to Set the Constructor Property when Changing the Prototype

Hay un efecto secundario crucial de configurar manualmente el prototype a un nuevo objeto.¡Borra la propiedad de constructor!. Esta propiedad se puede usar para verificar qué función constructora creó la instancia, pero dado que la propiedad se ha sobrescrito, ahora da resultados falsos:

duck.constructor === Bird; // false -- Oops
duck.constructor === Object; // true, all objects inherit from Object.prototype
duck instanceof Bird; // true, still works

Para solucionar esto, siempre que un prototype se configura manualmente en un nuevo objeto, recuerde definir la propiedad del constructor:

Bird.prototype = {
  constructor: Bird, // define the constructor property
  numLegs: 2,
  eat: function() {
    console.log("nom nom nom");
  },
  describe: function() {
    console.log("My name is " + this.name); 
  }
};

Defina la propiedad del constructor prototype en Dog.

El código con la solución se encuentra aquí:Download

Understand Where an Object’s Prototype Comes From

Al igual que las personas heredan genes de sus padres, un objeto hereda su prototype directamente de la función constructora que lo creó. Por ejemplo, aquí el constructor Bird crea el objeto duck:

function Bird(name) {
  this.name = name;
}

let duck = new Bird("Donald");

duck hereda su prototype de la función Bird. Puede mostrar esta relación con el método isPrototypeOf:

Bird.prototype.isPrototypeOf(duck);
// returns true

Use isPrototypeOf para verificar el prototipo de beagle.

El código con la solución se encuentra aquí:Download

Understand the Prototype Chain

Todos los objetos en JavaScript ( con algunas excepciones) tienen un prototype. Además, el prototipo de un objeto en sí es un objeto.

function Bird(name) {
  this.name = name;
}

typeof Bird.prototype; // => object

Debido a que un prototype es un objeto, ¡ un prototype puede tener su propio prototype !. En este caso, el prototype de Bird.prototype es Object.prototype:

Object.prototype.isPrototypeOf(Bird.prototype);
// returns true

¿Cómo es esto útil? Puede recordar el método hasOwnProperty de un desafío anterior:

let duck = new Bird("Donald");
duck.hasOwnProperty("name"); // => true

El método hasOwnProperty se define en Object.prototype, al que se puede acceder mediante Bird.prototype, al que luego se puede acceder por duck. Este es un ejemplo del prototype chain. En esta prototype chain, Bird es el supertype para duck, mientras que el duck es el subtype. Object es un supertype para Bird y duck. Object es un supertype para todos los objetos en JavaScript. Por lo tanto, cualquier objeto puede usar el método hasOwnProperty.

Modifique el código para mostrar la chain prototype correcta.

El código con la solución se encuentra aquí:Download

Use Inheritance So You Don't Repeat Yourself

Hay un principio en la programación llamado Don't Repeat Yourself (DRY). La razón por la que el código repetido es un problema es porque cualquier cambio requiere corregir el código en varios lugares. Esto generalmente significa más trabajo para los programadores y más espacio para errores.

Observe en el siguiente ejemplo que Bird y Dog comparten el método de descripción:

Bird.prototype = {
  constructor: Bird,
  describe: function() {
    console.log("My name is " + this.name);
  }
};

Dog.prototype = {
  constructor: Dog,
  describe: function() {
    console.log("My name is " + this.name);
  }
};

El método describe se repite en dos lugares. El código se puede editar para seguir el principio DRY creando un supertype (o padre) llamado Animal:

function Animal() { };

Animal.prototype = {
  constructor: Animal, 
  describe: function() {
    console.log("My name is " + this.name);
  }
};

Como Animal incluye el método de description, puedes eliminarlo de Bird y Dog:

Bird.prototype = {
  constructor: Bird
};

Dog.prototype = {
  constructor: Dog
};

El método eat se repite tanto en Cat como en Bear. Edite el código siguiendo el principio de DRY moviendo el método eat al supertipo Animal.

El código con la solución se encuentra aquí:Download

Inherit Behaviors from a Supertype

En el desafío anterior, creó un supertipo llamado Animal que definía comportamientos compartidos por todos los animales:

function Animal() { }
Animal.prototype.eat = function() {
  console.log("nom nom nom");
};

Este y el próximo desafío cubrirán cómo reutilizar los métodos de Animal dentro de Bird y Dog sin definirlos nuevamente. Utiliza una técnica llamada herencia. Este desafío cubre el primer paso: crear una instancia del supertitpo (o padre). Ya conoce una forma de crear una instancia de Animal utilizando el nuevo operador:

let animal = new Animal();

Existen algunas desventajas al usar esta sintaxis para la herencia, que son demasiado complejas para el alcance de este desafío. En cambio, aquí hay un enfoque alternativo sin esas desventajas:

let animal = Object.create(Animal.prototype);

Object.create(obj) crear un nuevo objeto y establece obj como el prototipo del nuevo objeto. Recuerde que el prototipo es como la "receta" para crear un objeto. Al configurar el prototype de Animal, efectivamente le está dando la instancia del animal la misma receta que cualquier otra instancia de Animal.

animal.eat(); // prints "nom nom nom"
animal instanceof Animal; // => true

Use Object.create para hacer dos instancias de Animal llamado duck y beagle.

El código con la solución se encuentra aquí:Download

Set the Child's Prototype to an Instance of the Parent

En el desafío anterior viste el primer paso para heredar el comportamiento del supertipo (o padre) Animal: crear una nueva instancia de Animal-

Este desafío cubre el siguiente paso: configurar el prototype del subtipo ( o niño) en este caso, Bird, para que sea una instancia de Animal.

Bird.prototype = Object.create(Animal.prototype);

Recuerde que el prototype es como la receta para crear un objeto. En cierto modo, la recete de Bird ahora incluye todos los "ingredientes" clave de Animal.

let duck = new Bird("Donald");
duck.eat(); // prints "nom nom nom"

duck hereda todas las propiedades de los Animales, incluido el método de aeat.

Modifique el código para que las instancias de Dog hereden de Animal.

El código con la solución se encuentra aquí:Download

Reset an Inherited Constructor Property

Cuando un objeto hereda su prototype de otro objeto, también hereda la propiedad constructora del supertipo.

Aquí hay un ejemplo:

function Bird() { }
Bird.prototype = Object.create(Animal.prototype);
let duck = new Bird();
duck.constructor // function Animal(){...}

Pero duck y todas las instancias de Bird deberían mostrar que fueron construidas por Bird y no por Animal. Para hacerlo, puede establecer manualmente la propiedad del constructor de Bird en el objeto Bird:

Bird.prototype.constructor = Bird;
duck.constructor // function Bird(){...}

Arregle el código para duck.constructor y beagle.constructor devuelvan sus respectivos constructores.

El código con la solución se encuentra aquí:Download

Add Methods After Inheritance

Una función constructora que hereda su objeto prototype de una función constructora de supertipo puede tener sus propios métodos además de los métodos heredados.

Por ejemplo, Bird es un constructor que hereda su prototipo de Animal:

function Animal() { }
Animal.prototype.eat = function() {
  console.log("nom nom nom");
};
function Bird() { }
Bird.prototype = Object.create(Animal.prototype);
Bird.prototype.constructor = Bird;

Además de lo que se hereda de Animal, desea agregar un comportamiento que sea exclusivo de los objetos Bird. Aquí, Bird obtendrá una función fly(). Las funciones se agregan al prototipo de Bird de la misma manera que cualquier función de constructor:

Bird.prototype.fly = function() {
  console.log("I'm flying!");
};

Ahora las instancias de Bird tendrán los métodos eat() y fly():

let duck = new Bird();
duck.eat(); // prints "nom nom nom"
duck.fly(); // prints "I'm flying!"

Agregue todo el código necesario para que el objeto Dog herede de Animal y el prototipo de constructor Dog esté configurado en Dog. Luego agregue un método bark() al objeto Dog para que el beagle pueda eat() y bark(). El método bark() debería imprimir "Woof" en la consola.

El código con la solución se encuentra aquí:Download

Override Inherited Methods

En lecciones anteriores, aprendió que un objeto puede heredar su comportamiento (métodos) de otro objeto haciendo referencia a su objeto prototype:

ChildObject.prototype = Object.create(ParentObject.prototype);

Luego, ChildObject recibió sus propios métodos al encadenarlos en su prototipo:

ChildObject.prototype.methodName = function() {...};

Es posible anular un método heredado. Se hace de la misma manera: agregando un método a ChildObject.prototype usando el mismo nombre de método que el que se anula. Aquí hay un ejemplo de Bird que anula el método eat() heredado de Animal:

function Animal() { }
Animal.prototype.eat = function() {
  return "nom nom nom";
};
function Bird() { }

// Inherit all methods from Animal
Bird.prototype = Object.create(Animal.prototype);

// Bird.eat() overrides Animal.eat()
Bird.prototype.eat = function() {
  return "peck peck peck";
};

Si tiene una instancia let duck = new Bird(); y llamas a duck.eat() , así es como JavaScript busca el método en la cadena de prototipos de duck:

  1. duck => Is eat() defined here? No.

  2. Bird => Is eat() defined here? => Yes. Execute it and stop searching.

  3. Animal => eat() is also defined, but JavaScript stopped searching before reaching this level.

4 . Object => JavaScript stopped searching before reaching this level.

Anule el método fñy() para Penguin para que devuelva "Alas, this is a flightless bird."

El código con la solución se encuentra aquí:Download

Use a Mixin to Add Common Behavior Between Unrelated Objects

Como has visto el comportamiento se comparte a través de la herencia. Sin embargo, hay casos en que la herencia no es la mejor solución. La herencia no funciona bien para objetos no relacionados como Bird y Airplane. Ambos pueden fly, pero un pájaro no es un tipo de avión y viceversa.

Para objetos no relacionados, es mejor usar mixins. Un mixin permite que otros objetos usen una colección de funciones.

let flyMixin = function(obj) {
  obj.fly = function() {
    console.log("Flying, wooosh!");
  }
};

flyMixin toma cualquier objeto y le da el método fly.

let bird = {
  name: "Donald",
  numLegs: 2
};

let plane = {
  model: "777",
  numPassengers: 524
};

flyMixin(bird);
flyMixin(plane);

Aquí el bird y plane pasan a flyMixin, que luego asigna la función de vuelo a cada objeto. Ahora el bird y plane pueden volar:

bird.fly(); // prints "Flying, wooosh!"
plane.fly(); // prints "Flying, wooosh!"

Observe cómo el mixin permite que el mismo método de fly sea reutilizado por los objetos no relacionados, bird y plane.

Cree un mixin llamado glideMixin que defina un método llamado glide. Luego use el glideMixin para dar a bird y boat la capacidad de deslizarse.

El código con la solución se encuentra aquí:Download

Use Closure to Protect Properties Within an Object from Being Modified Externally

En el desafío anterior, el bird tenía un name. Se considera público porque se puede acceder y cambiar fuera de la definición de bird.

bird.name = "Duffy";

Por lo tanto, cualquier parte de su código puede cambiar fácilmente el nombre de bird a cualquier valor. Piense en cosas como las contraseñas y las cuentas bancarias que se pueden cambiar fácilmente por cualquier parte de su base de código. Eso podría causar muchos problemas.

La forma más sencilla de hacer que esta propiedad pública sea privada es creando una variable dentro de la función constructora. Esto cambia el alcance de esa variable para que esté dentro de la función de constructor versus disponible globalmente. De esta manera, solo se puede acceder a la variable y cambiarla por métodos también dentro de la función constructora.

function Bird() {
  let hatchedEgg = 10; // private variable

  /* publicly available method that a bird object can use */
  this.getHatchedEggCount = function() { 
    return hatchedEgg;
  };
}
let ducky = new Bird();
ducky.getHatchedEggCount(); // returns 10

Aquí getHatchedEggCount es un método privilegiado, porque tiene acceso a la variable privada hatchedEgg. Esto es posible porque hatchedEgg se declara en el mismo contexto que getHatchedEggCount . En JavaScript, una función siempre tiene acceso al contexto en el que se creó. Esto se llama closure.

Cambie como se declara weight en la función Bird para que sea una variable privada. Luego, cree un método getWeight que devuelva el valor de weight 15.

El código con la solución se encuentra aquí:Download

Understand the Immediately Invoked Function Expression (IIFE)

Un patrón común en JavaScript es ejecutar una función tan pronto como se declare:

(function () {
  console.log("Chirp, chirp!");
})(); // this is an anonymous function expression that executes right away
// Outputs "Chirp, chirp!" immediately

Tenga en cuenta que la función no tiene nombre y no se almacena en una variable. Los dos paréntesis () al final de la expresión de función hacen que se ejecute o invoque de inmediato. Este patrón se conoce como una expresión de función invocada inmediatamente o IIFE.

Vuelve a escribir la función makeNest y elimine su llamada para que sea una expresión de función invocada inmediatamente anónima (IIFE).

El código con la solución se encuentra aquí:Download

Use an IIFE to Create a Module

Una expresión de función invocada inmediatamente (IIFE) se usa a menudo para agrupar funcionalidades relacionadas en un solo objeto o módulo. Por ejemplo, un desafío anterior definió dos mixins:

function glideMixin(obj) {
  obj.glide = function() {
    console.log("Gliding on the water");
  };
}
function flyMixin(obj) {
  obj.fly = function() {
    console.log("Flying, wooosh!");
  };
}

Podemos agrupar estos mixims en un módulo de la siguente manera:

let motionModule = (function () {
  return {
    glideMixin: function(obj) {
      obj.glide = function() {
        console.log("Gliding on the water");
      };
    },
    flyMixin: function(obj) {
      obj.fly = function() {
        console.log("Flying, wooosh!");
      };
    }
  }
})(); // The two parentheses cause the function to be immediately invoked

Tenga en cuenta que tiene una expresión de función invocada inmediatamente (IIFE) que devuelve un objeto motionModule. Este objeto devuelto contiene todos los comportamientos de mixin como propiedades del objeto. La ventaja del patrón el módulo es que todos los comportamientos de movimiento se pueden empaquetar en un solo objeto que luego pueden usar otras partes de su código. Aquí hay un ejemplo usándolo:

motionModule.glideMixin(duck);
duck.glide();

Cree un módulo llamado funModule para envolver los dos mixins isCuteMixin y singMixin. funModule debería devolver un objeto.

El código con la solución se encuentra aquí:Download