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
- Use Dot Notation to Access the Properties of an Object
- Create a Method on an Object
- Make Code More Reusable with the this Keyword
- Define a Constructor Function
- Use a Constructor to Create Objects
- Extend Constructors to Receive Arguments
- Verify an Object's Constructor with instanceof
- Understand Own Properties
- Use Prototype Properties to Reduce Duplicate Code
- Iterate Over All Properties
- Change the Prototype to a New Object
- Remember to Set the Constructor Property when Changing the Prototype
- Understand Where an Object’s Prototype Comes From
- Understand the Prototype Chain
- Use Inheritance So You Don't Repeat Yourself
- Inherit Behaviors from a Supertype
- Set the Child's Prototype to an Instance of the Parent
- Reset an Inherited Constructor Property
- Add Methods After Inheritance
- Override Inherited Methods
- Use a Mixin to Add Common Behavior Between Unrelated Objects
- Use Closure to Protect Properties Within an Object from Being Modified Externally
- Understand the Immediately Invoked Function Expression (IIFE)
- Use an IIFE to Create a Module
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:
-
duck => Is eat() defined here? No.
-
Bird => Is eat() defined here? => Yes. Execute it and stop searching.
-
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