Sprint 8
Sprint 8
Capítulo 1/10
Intro
Introducción
El centro comunitario de TripleTen se está construyendo, a paso lento pero
seguro. Y hemos conseguido que sea lo suficientemente seguro para que los
niños de la zona vengan a jugar, y es por esto por lo que los voluntarios han
montado un humilde pero agradable parque infantil. Qué casualidad: hoy, el
arquitecto Amir tiene que cuidar a su sobrina, Nabila. Amir ha venido temprano a
trabajar para esperar a que los padres de Nabila la trajeran y de paso asegurarse
de que el parque infantil estuviese listo para todo lo que la mente creativa de la
niña pudiese imaginar. Al igual que Nabila, estás a punto de probar un nuevo
ámbito en el que jugar: el "sandbox" de las clases de JavaScript.
Sprint 8
Capítulo 2/10
Convirtiéndote en desarrollador de front-end
Convirtiéndote en desarrollador de front-
end
¡Felicidades, has llegado a la mitad del programa! Para celebrarlo, echemos un vistazo
desde una perspectiva general.
Los salarios oscilan entre los 50.000 y los 70.000 dólares, dependiendo de la zona
geográfica, el tamaño de la empresa y la experiencia previa.
Una progresión natural a partir de este punto sería convertirse en un desarrollador front-end
de nivel medio, luego en un ingeniero de software, y después en un ingeniero de software
senior. Dicho esto, al añadir habilidades de back-end (que lo harás, un poco más adelante
en este programa), esencialmente duplicas tu valor de mercado, ¡convirtiéndote en un
desarrollador full stack en el proceso!
Los próximos tres sprints se dedicarán a seguir desarrollando las habilidades que
constituyen el núcleo de cualquier papel de desarrollador web, especialmente en el front-
end.
Sprint 8
Capítulo 3/10
Programación Orientada a Objetos
Introducción a la programación orientada
a objetos
De mayor, Nabila quiere ser arquitecta como su tío. Para el primer proyecto de su porfolio,
va a construir una maqueta del centro comunitario con lego. Empezó sacando todos los
bloques de la caja e improvisando con ellos, pero eso no acabó del todo bien. Para ayudar a
su sobrina, Amir le entregó un manual detallado. Esto también se aplica a temas más
complejos como la programación orientada a objetos (POO), es importante aprender la
teoría antes de pasar a la práctica. Esto es exactamente lo que vas a tratar en este capítulo,
aprender sobre los principios fundamentales de POO: encapsulación, herencia y
polimorfismo.
A medida que el código dentro de tu programa crece, se hace más difícil añadir
funcionalidades, corregir errores o trabajar en general. Tal vez ya lo hayas notado mientras
trabajabas en tus proyectos.
En el pasado, los programadores escribían código destinado a ser leído únicamente por los
ordenadores. Pero, con el paso del tiempo, surgió la necesidad de organizar el código de
forma que también permitiera trabajar con él. Para ello, se determinó que, en lugar de ser
una simple secuencia de comandos, el código debía estructurarse en bloques lógicos.
Así que, los programadores empezaron a darle vueltas sobre cómo hacerlo y así fue como
aparecieron los paradigmas de programación. Un paradigma de programación es un
enfoque de la codificación basado en un conjunto coherente de principios compartidos en
una disciplina concreta. Básicamente, crea un estándar para todos. Tener distintos enfoques
es útil para resolver distintos tipos de problemas.
Hoy en día, existen numerosos paradigmas, pero la programación orientada a objetos es la
más popular, y es la que trataremos en este curso.
Según POO, el código debe cumplir los siguientes requisitos:
Pero primero, repasemos los fundamentos de POO y hablemos de cómo podemos trabajar
con objetos, sus métodos y sus clases.
Capítulo 3/10
Programación Orientada a Objetos
Objetos en POO
Vamos a explorar los objetos desde el punto de vista de la programación orientada a
objetos.
Podemos considerar casi cualquier cosa como un objeto. Por ejemplo, el dispositivo en el
que estás leyendo esta lección puede considerarse un objeto. Podemos describir algunas de
sus propiedades, como el color, el tamaño y el brillo de la pantalla. Del mismo modo,
también podemos describir una acción mediante una función. Por ejemplo, podemos
encender y apagar nuestros dispositivos o pulsar una tecla. A veces, las propiedades y los
métodos de los objetos de nuestro dispositivo están relacionados. Por ejemplo, si pulsamos
una tecla para ajustar el volumen, afectará a la propiedad "volumen".
Las funciones declaradas dentro de los objetos se denominan métodos. Un método, por lo
general, es cualquier función que se realiza sobre un objeto. También debemos señalar que
cualquier función en JavaScript es un método, incluso si se declara globalmente. En este
caso, es un método del objeto global. La función global alert() también puede ser llamada
como window.alert().
Los métodos son funciones, y ya hemos utilizado métodos muchas veces. Por ejemplo, ya
hemos conocido los métodos split() e indexOf() del tipo de datos string, y pronto exploraremos
formas de crear nuestros propios métodos.
Al igual que podemos pensar en nuestro dispositivo electrónico favorito como un objeto,
también podemos pensar en las cosas no físicas como objetos. Por ejemplo, podemos
describir una canción de la lista de reproducción que hicimos en el capítulo del DOM como
un objeto:
Copiar códigoJAVASCRIPT
const song = {
title: "Circles",
artist: "Mac Miller",
isLiked: false,
like: function () {
song.isLiked = !song.isLiked;
}
};
El objeto almacena datos en las propiedades title, artist, y isLiked. También tiene un
método like que le añade cierta funcionalidad. Los objetos sirven para encapsular datos y
funcionalidad en una sola entidad, presentándolos en un formato conveniente sobre el que
podemos construir.
En este contexto, los datos y la funcionalidad suelen denominarse "estado" y
"comportamiento". A continuación, entramos en detalle sobre estos conceptos:
Esto resume las ideas claves de la programación orientada a objetos (POO). Se trata
esencialmente de combinar nuestros datos y la funcionalidad del programa dentro de los
objetos.
Sprint 8
Capítulo 3/10
Programación Orientada a Objetos
Datos y funcionalidad
Entremos un poco más en detalle sobre la utilización de objetos en POO.
En la lección anterior, explicamos que los objetos permiten almacenar datos y programar la
funcionalidad en un solo lugar.
Volvamos al ejemplo de la lista de reproducción para aplicar dicho concepto:
Nuestra genial lista de reproducción
Nuestro objeto song fue creado con una estructura estricta de atributos, es decir, propiedades
y métodos. Si queremos crear otro objeto song, tendrá que tener una estructura similar:
Copiar códigoJAVASCRIPT
const song = {
title: "Diary of Jane",
artist: "Breaking Benjamin",
isLiked: false,
like: function () {
song.isLiked = !song.isLiked;
}
}
El objeto song tiene las propiedades title, artist y isLiked, que podemos considerar como los
datos, mientras que el método like() representa la funcionalidad del objeto. Este
objeto song proporciona una forma de agrupar los datos y la funcionalidad de una canción
en una sola entidad.
Pero, naturalmente, una lista de reproducción suele tener varias canciones, así que vamos a
crear un objeto para cada una de ellas:
Copiar códigoJAVASCRIPT
const song1 = {
title: "Chanel",
artist: "Frank Ocean",
isLiked: false,
like: function () {
song1.isLiked = !song1.isLiked;
}
};
const song2 = {
title: "Circles",
artist: "Mac Miller",
isLiked: false,
like: function () {
song2.isLiked = !song2.isLiked;
}
};
const song3 = {
title: "Until I Walk Through The Flames",
artist: "Wicca Phase Springs Eternal",
isLiked: true,
like: function () {
song3.isLiked = !song3.isLiked;
}
};
Hemos creado tres objetos manualmente. Muy fácil, ¿verdad? Pero imagina que trabajas
con una lista de reproducción de 1.000 canciones. Para ello habría que crear 1.000 objetos.
Ya sabes que las funciones nos permiten evitar este tipo de tareas repetitivas. Escribamos
una función createSong() para crear nuevos objetos song:
Copiar códigoJAVASCRIPT
/* declara la función createSong,
que devolverá nuevos objetos song */
function createSong(title, artist) {
// crea un objeto llamado newSong
const newSong = {
title,
artist,
isLiked: false,
like: function () {
newSong.isLiked = !newSong.isLiked;
}
}
return newSong;
}
Estamos llamando a method() sobre el objeto obj, por lo que obj se mostrará en la consola.
En el siguiente ejemplo, el método like() devolverá el valor de la propiedad isLiked dentro del
objeto song1:
Copiar códigoJAVASCRIPT
/* en este caso, dentro de la función like()
el valor de "this" será el objeto song1,
ya que estamos llamando al método like() en el objeto song */
song1.like();
El valor de this depende únicamente del objeto sobre el que se llama a la función, y no del
objeto en el que se almacena. En nuestro ejemplo, el valor de this es el objeto song1. Esto es
debido a que este es el objeto sobre el que se llama a la función como método.
Prueba a adivinar cuál será el valor de this en el siguiente ejemplo, dentro de las
funciones like():
Copiar códigoJAVASCRIPT
song2.like();
song3.like();
Una vez más, si la función se llama como un método de objeto, entonces en el momento de
la llamada, el valor de this dentro de esa función será el objeto sobre el que se llamó la
función.
Por lo tanto, la respuesta es:
Copiar códigoJAVASCRIPT
song2.like(); // en este caso, el valor de "this" será song2
song3.like(); // y en este, el valor de "this" será song3
Ahí lo tienes, tres lecciones de teoría POO repletas de acción. Empecemos a practicar.
Sprint 8
Capítulo 3/10
Programación Orientada a Objetos
Clases
Puedes seguir leyendo si lo prefieres, pero mediante el vídeo a continuación te
plasmamos una idea general sobre el tema que trataremos en esta lección, échale
un vistazo:
El código de inicio se encuentra en este replit (materiales en inglés).
Ahora ya sabes cómo crear objetos con propiedades únicas donde nuestra
funcionalidad se almacena en un solo lugar y está disponible para que cualquier
objeto pueda acceder a ella. Este enfoque nos permite reducir el uso de memoria
de la aplicación. En esta lección, nos centraremos en cómo conseguir el mismo
resultado utilizando clases.
La palabra clave class
Una clase define los datos y la funcionalidad que poseerá un objeto. Piensa en
ello como un plano utilizado para crear objetos. Estos objetos se denominan
instancias de clase. Las clases declaran tanto propiedades (datos) como métodos
(funcionalidad) en sus objetos.
Vamos a crear el objeto song que construimos anteriormente llamando a la
función createSong(), pero esta vez, vamos a utilizar la sintaxis de clase que se
introdujo en ES6 (ES2015).
ES6 es la sexta versión de la especificación ECMAScript introducida en 2015.
Por eso a veces también se le conoce como ES2015.
Para definir una clase, tendremos que escribir la palabra clave class, seguida del
nombre de una variable a la que podamos acceder para utilizar la clase. En
muchos lenguajes de programación, incluido JavaScript, los nombres de nuestras
clases deben comenzar con una letra mayúscula. Todo seguirá funcionando bien
si el nombre de tu clase empieza con una letra minúscula, pero es mejor seguir
esta convención de nomenclatura para que sea fácil determinar qué variables
contienen clases:
Copiar códigoJAVASCRIPT
class Song {
constructor(title, artist) {
this.title = title;
this.artist = artist;
}
like() {
this.isLiked = !this.isLiked;
}
}
El método constructor()
Cada clase contiene el método constructor(), el cual es llamado cada vez que se
inicializa un nuevo objeto de una clase. El método constructor() rellena nuestro
futuro objeto con datos.
La expresión new Song("Start Over", "Any Given Day") devuelve un nuevo objeto. El
operador new se utiliza para iniciar una clase. El uso de esta palabra clave muestra
que la clase iniciada devolverá un nuevo objeto, que es una instancia de esta
clase. Si intentamos iniciar la clase sin new, se producirá un error, ya que el
método constructor() no puede ser llamado sin el operador new.
En el ejemplo anterior, la palabra clave this dentro del método constructor() actúa
como un enlace al objeto devuelto. Esto significa que el valor de nuestros
argumentos de entrada, title y artist, se almacenarán en las
propiedades this.title y this.artist del objeto que almacenamos en la variable song.
Podemos omitir la descripción del método constructor() dentro de la clase. En ese
caso, constructor() se creará implícitamente como un método con un cuerpo
vacío constructor() {}. En consecuencia, provoca que no se añadan datos
adicionales a los nuevos objetos.
Aparte del método constructor(), en el que se añaden propiedades a un nuevo
objeto, las clases pueden tener otros métodos. Todos los objetos creados con new
Song() podrán acceder a estos métodos:
Copiar códigoJAVASCRIPT
const song1 = new Song("Start Over", "Any Given Day");
const song2 = new Song("Bitter End", "The Veer Union");
Sprint 8
Capítulo 3/10
Programación Orientada a Objetos
Encapsulación
En el mundo actual, estamos constantemente rodeados de todo tipo de dispositivos, pero
esto no significa necesariamente que tengamos un conocimiento profundo de los mismos.
No necesitamos saber qué pasa cuando encendemos nuestro PC. Nos basta con saber que
para ello solo necesitamos pulsar el botón de encendido. En programación, este botón se
define como una parte de la interfaz de un PC.
El funcionamiento interno de un programa suele estar oculto al mundo exterior. A este
hecho lo llamamos encapsulación. La principal ventaja es que oculta la complejidad del
dispositivo, dejando al usuario una forma cómoda e intuitiva de interactuar con él.
Interfaz de usuario
En programación, a menudo nos encontramos con el término "interfaz de usuario". Cuando
hablamos de una interfaz de usuario, nos referimos a la apariencia del programa. Esto
incluye cosas como el menú de usuario, cualquier botón o interruptor, campos de entrada,
etc. Es lo que permite a nuestros usuarios interactuar con un programa, y actúa como medio
entre el código del programa y el usuario.
Todo dispositivo complejo debe tener una interfaz fácil de usar. Es mucho más sencillo
arrancar un coche con una llave que levantar el capó e intentar arrancar el motor
manualmente.
#getAvailableGasValue(gasValue) {
if (gasValue < 0) return 0;
if (gasValue > this.#maxGasTankValue) return this.#maxGasTankValue;
return gasValue;
}
refuel(gasValue) {
this.#gasTankValue = this.#getAvailableGasValue(gasValue);
}
getDistance() {
return this.#gasTankValue / this.#fuelConsumption * 100;
}
}
_getAvailableGasValue(gasValue) {
if (gasValue < 0) return 0;
if (gasValue > this._maxGasTankValue) return this._maxGasTankValue;
return gasValue;
}
refuel(gasValue) {
this._gasTankValue = this._getAvailableGasValue(gasValue);
}
getDistance() {
return this._gasTankValue / this._fuelConsumption * 100;
}
}
Sprint 8
Capítulo 3/10
Programación Orientada a Objetos
Herencia
En la programación orientada a objetos, la herencia significa crear una clase basada en otra
clase.
En nuestra vida, todos hemos podido observar cómo funciona el concepto de herencia.
transmisión de rasgos de padres a hijos.
Estos mismos principios también tienen cabida en el mundo de la programación. Ya lo has
visto en acción. Cuando estudiamos HTML y CSS, viste que los estilos aplicados a un
elemento padre son heredados por sus elementos hijos. En JavaScript, la herencia permite a
los programadores reutilizar clases escritas previamente. Podemos tener clases hijo o clases
padre, también denominadas subclases y superclases respectivamente. Esto les ayuda a
eliminar la excesiva duplicación de código y hace que sea mucho más fácil de mantener.
Veamos el siguiente ejemplo para entenderlo mejor:
Copiar códigoJAVASCRIPT
class Student {
constructor(name, group) {
this._name = name;
this._group = group;
this._profession = null;
this._trainingDuration = null;
}
getInfo() {
return {
name: this._name,
group: this._group,
profession: this._profession,
trainingDuration: this._trainingDuration
}
}
}
Aquí tenemos la clase Student, que crea un objeto que almacena información sobre un
estudiante. La clase toma dos argumentos en la inicialización; el nombre del estudiante, y el
número de su grupo. Las propiedades sobre la profesión (this._profession) y la duración de la
formación (this._trainingDuration) reciben el valor null.
Si aplicamos las reglas de lectura y escritura de valores en propiedades privadas de la
lección anterior, no deberíamos cambiar las propiedades this._profession y this._trainingDuration.
Pero ahora mismo, un objeto de la clase Student no tiene las interfaces públicas necesarias
para cambiar estas propiedades privadas.
De hecho, no necesitamos estas interfaces públicas. Por supuesto, podríamos añadir más
parámetros a la clase Student, y luego asignar estos valores
a this._profession y this._trainingDuration, de la siguiente manera:
Copiar códigoJAVASCRIPT
class Student {
constructor(name, group, profession, trainingDuration) {
this._name = name;
this._group = group;
this._profession = profession;
this._trainingDuration = trainingDuration;
}
}
Sin embargo, es mejor que intentemos pasar el mínimo número de argumentos posible
cuando estemos creando instancias de clase.
A veces, queremos que un valor permanezca igual, independientemente de los valores que
el usuario pase al método constructor. Se denominan valores estáticos. Entonces, ¿cómo
podemos crear objetos con propiedades privadas que contengan valores estáticos sin utilizar
los parámetros de constructor()? Una opción sería crear una clase separada para cada tipo de
objeto:
Copiar códigoJAVASCRIPT
// clase para estudiantes del curso de Desarrollador Web
class WebDeveloperStudent {
constructor(name, group) {
this._name = name;
this._group = group;
this._profession = "Web Developer";
this._trainingDuration = 10;
}
getInfo() {
return {
name: this._name,
group: this._group,
profession: this._profession,
trainingDuration: this._trainingDuration
}
}
}
getInfo() {
return {
name: this._name,
group: this._group,
profession: this._profession,
trainingDuration: this._trainingDuration
}
}
}
// clase para estudiantes del curso de Analista de Datos
class DataAnalystStudent {
constructor(name, group) {
this._name = name;
this._group = group;
this._profession = "Data Analyst";
this._trainingDuration = 6;
}
getInfo() {
return {
name: this._name,
group: this._group,
profession: this._profession,
trainingDuration: this._trainingDuration
}
}
}
Ahora tenemos 3 clases separadas que no están vinculadas a la antigua clase Student de
ninguna manera. La desventaja de este código es que tenemos que escribir todas las
propiedades que consideramos repetitivas y el método getInfo() en cada clase. Las líneas que
definen los valores de this._name y this._group son también las mismas en las tres clases.
Además, si necesitamos modificar el método getInfo(), tendremos que hacerlo tres veces, una
para cada clase.
Podemos resolver el problema de la duplicación de código en nuestras clases mediante la
herencia. Podemos utilizar la palabra clave extends para crear nuevas clases que heredarán
todas las propiedades y métodos de sus clases padre:
Copiar códigoJAVASCRIPT
class WebDeveloperStudent extends Student {
}
Ahora hemos creado tres clases de estudiantes en función de la profesión a la que se
quieren dedicar. Las tres clases heredan de la misma clase padre Student porque hemos
utilizado la palabra clave extends.
Según la tarea inicial, las clases deben tener sus propios valores únicos para las
propiedades _profession y _trainingDuration. Esto significa que tenemos que llamar al
método constructor() en cada clase y asignar los valores correctos para las
propiedades _profession y _trainingDuration.
super() es llamado en cada clase de constructor heredada de la clase Student. La palabra
clave super nos permite hacer una referencia a la clase padre de la que hereda la clase hijo.
Escribir super() llama al método constructor() de la clase padre. En este caso, se trata de la
clase Student.
Copiar códigoJAVASCRIPT
class WebDeveloperStudent extends Student {
constructor(name, group) {
super(name, group);
this._profession = "Desarrollador web";
this._trainingDuration = 10;
}
}
student1.getInfo();
/*
{
name: "Wendy Webberton",
group: 1,
profession: "Web developer",
traningDuration: 10
}
*/
student2.getInfo();
/*
{
name: "Dennis Databerg",
group: 3,
profession: "Data analyst",
traningDuration: 6
}
*/
Puede ser un poco difícil absorber toda esta información simplemente leyendo. Pero no te
preocupes, con algo de práctica se verá todo más claro.
Sprint 8
Capítulo 3/10
Programación Orientada a Objetos
Polimorfismo
En la lección anterior, tratamos el tema de la herencia en JavaScript y estudiamos un
ejemplo en el cual las clases hijo heredaban el método getInfo() de la clase de sus padres.
Este mecanismo hace que trabajar con clases sea mucho más conveniente. Podemos
declarar un método una sola vez y luego usarlo con cada instancia de nuestras clases hijas.
Echémosle otro vistazo:
Copiar códigoJAVASCRIPT
class Student {
constructor(name, group) {
this._name = name;
this._group = group;
this._profession = null;
this._trainingDuration = null;
}
getInfo() {
return {
name: this._name,
group: this._group,
profession: this._profession,
trainingDuration: this._trainingDuration
}
}
}
getInfo() {
return {
name: this._name,
group: this._group,
profession: this._profession,
trainingDuration: this._trainingDuration,
language: "Javascript"
}
}
}
El método getInfo() de la subclase WebDeveloperStudent es ahora diferente del método getInfo() de
la clase padre Student.
Esto significa que cuando llamemos al método getInfo() para la clase WebDeveloperStudent, se
invocará la función que escribimos para nuestra subclase desarrollador web en lugar del
método getInfo() de la superclase Student.
Copiar códigoJAVASCRIPT
const studentWithoutProfession = new Student("Mike Realperson", 10);
const student1 = new WebDeveloperStudent("Wendy Webberton", 11);
const student2 = new WebDeveloperStudent("Mary Webster", 6);
getInfo() {
return {
name: this._name,
group: this._group,
profession: this._profession,
trainingDuration: this._trainingDuration,
language: "Python"
}
}
}
Si programamos nuestras clases WebDeveloperStudent y PythonDeveloperStudent de esta manera,
esto nos permitirá utilizar con seguridad nuestra función getInfoList() para poder obtener
información sobre nuestros estudiantes. Además, la instancia de la
clase DesignerStudent seguirá utilizando el método getInfo() de la clase Student:
Copiar códigoJAVASCRIPT
getInfoList([student1, student2, student3]);
Polimorfismo
Acabas de ver el concepto de polimorfismo en acción, este se basa en la capacidad de hacer
que objetos con la misma interfaz tengan una implementación diferente.
La función getInfoList() llama al método getInfo() de las instancias pasadas en secuencia, y la
implementación de dicho método dependerá de la clase de la instancia.
Puede que te hayas dado cuenta de que anular un método en nuestro ejemplo ha llevado a
una excesiva duplicación de código. La parte principal del código relativo al
método getInfo() se sigue repitiendo en cada clase hija. Esto se puede arreglar fácilmente. No
solo podemos anular el método de la clase padre, también podemos extenderlo.
Si utilizamos la palabra clave super en el método de la clase hija, podemos seguir utilizando
la funcionalidad de la clase padre.
Una vez que anulamos el método getInfo() en la clase hija, escribir this.getInfo() será igual a
llamar al método de la clase hija. Pero aún podemos llamar a la función getInfo() de la clase
padre. Podemos hacerlo llamando a super.getInfo() en lugar de this.getInfo().
En el siguiente ejemplo, super.getInfo() llama al método getInfo() de la clase Student:
Copiar códigoJAVASCRIPT
class WebDeveloperStudent extends Student {
constructor(name, group) {
super(name, group);
this._profession = "Web Developer";
this._trainingDuration = 10;
}
getInfo() {
return super.getInfo();
}
}
es un método de la clase padre Student, que devuelve un objeto con información
super.getInfo()
del estudiante.
De hecho, el código anterior funciona de la misma manera que lo haría sin anular ningún
método. En el ejemplo anterior, podrías eliminar sin problemas la implementación del
método getInfo() de la clase WebDeveloperStudent, y nada cambiaría. La
clase WebDeveloperStudent simplemente heredará el método de la clase padre.
Copiar códigoJAVASCRIPT
class WebDeveloperStudent extends Student {
constructor(name, group) {
super(name, group);
this._profession = "Desarrollador web";
this._trainingDuration = 10;
}
}
getInfo() {
const info = super.getInfo();
info.language = "Javascript";
return info;
}
}
Llamar a super.getInfo() devuelve el resultado de getInfo() de la clase padre Student, que en este
caso, es un objeto. A continuación, asignamos ese objeto a la variable info, y después
añadimos la propiedad language a ese objeto. Finalmente, el método getInfo() de la clase
hija WebDeveloperStudent devuelve el objeto info actualizado. Así es como podemos ampliar
nuestros métodos, ¡es eficiente y se ve genial!
Sprint 8
Capítulo 3/10
Programación Orientada a Objetos
Sprint 8
Capítulo 4/10
Interfaces en POO — Parte 1
// Vamos a crear una instancia de una tarjeta con un texto único y una imagen de usuario
const card = new Card("¡Hola! ¿Cómo estás?", "userpic.jpg");
Ten en cuenta que nuestro constructor se va a volver mucho más complejo a lo largo del
proceso de desarrollo. Por ahora, nuestra clase solo contiene datos, que deben ser únicos
para cada mensaje en el chat.
Plantilla de marcado
Nuestra siguiente tarea es hacer que la clase Card devuelva nuestro marcado. Tuviste la
misma tarea en el proyecto "Alrededor de los Estados Unidos" donde tu función tenía que
devolver un elemento del DOM.
En la POO, esta función se convertirá en un método de la clase. Llamemos a nuestro
método _getTemplate():
Copiar códigoJAVASCRIPT
class Card {
constructor(text, image) {
this._text = text;
this._image = image;
}
_getTemplate() {
// realiza todas las acciones necesarias para obtener el marcado aquí
}
}
Sprint 8
Capítulo 4/10
Interfaces en POO — Parte 1
return cardElement;
}
generateCard() {
// Almacenar el marcado en el campo privado _element
// para que otros elementos puedan acceder a él
this._element = this._getTemplate();
// Añadir datos
this._element.querySelector(".card__avatar").src = this._image;
this._element.querySelector(".card__paragraph").textContent = this._text;
// Devolver el elemento
return this._element;
}
Ahora, vamos a crear un método que itere sobre los elementos del array messageList para
crear nuevas instancias de la clase Card y añadirlas al DOM:
Copiar códigoJAVASCRIPT
// inicio del archivo index.js
const messageList = [
{
image:
"https://practicum-content.s3.us-west-1.amazonaws.com/web-code/moved_card__image.jpg",
text: "Hola, ¡tenemos que configurar nuestro chat lo antes posible!"
},
{
image: "https://practicum-content.s3.us-west-1.amazonaws.com/web-code/moved_card__image-
lake.jpg",
text: "¡Ahora podemos crear tantas tarjetas como necesitemos!"
},
];
class Card {
// código de la clase
}
messageList.forEach((item) => {
// Crea una instancia de tarjeta
const card = new Card(item.text, item.image);
// Rellena la tarjeta y devuélvela
const cardElement = card.generateCard();
// Agrégala al DOM
document.body.append(cardElement);
});
Aquí está el resultado:
Ahora es realmente fácil añadir nuevas tarjetas, ya que lo único que tenemos que hacer es
añadir datos al array. ¡Misión cumplida! Hemos realizado una diferenciación de funciones.
Nuestros datos están gestionados dentro de nuestro array, mientras que nuestra clase está
gestionando la visualización de esos datos. De este modo, todo quedará claro a medida que
nuestro proyecto continúe desarrollándose.
Puedes ver el código final en repl.it (materiales en inglés). Haz clic en "ejecutar" para ver
cómo funciona
Sprint 8
Capítulo 4/10
Interfaces en POO — Parte 1
document.body.append(cardElement);
});
Puedes ver que, al ser tan numerosos, los argumentos que estamos pasando al crear
nuestra Card se nos van un poco de las manos. Tener tantos parámetros puede ser confuso y
además perjudica la legibilidad de nuestro código. ¡Pero hay una solución! Podemos pasar
un objeto cuando creamos una clase, y luego, asignar nuestras propiedades dentro de la
propia clase:
Copiar códigoJAVASCRIPT
class Card {
constructor(data) { // ahora el constructor obtiene un objeto
this._text = data.text;
this._image = data.image;
}
_getTemplate() {
const cardElement = document
.querySelector(this._cardSelector) // utiliza this._cardSelector aquí
.content
.cloneNode(true);
return cardElement;
}
}
Como ahora determinamos el tipo de tarjeta según su modificador en el elemento de
plantilla, estamos listos para crear nuevos tipos de tarjetas:
Copiar códigoHTML
<template class="card-template card-template_type_default">
<div class="card">
<img src="" alt="Userpic" class="card__avatar">
<div class="card__text">
<p class="card__paragraph"><!--el texto del mensaje aparecerá aquí --></p>
</div>
</div>
</template>
De hecho, ahora la clase puede trabajar con una plantilla completamente nueva:
Copiar códigoJAVASCRIPT
messageList.forEach((item) => {
// pasa el selector de plantilla en la creación
const card = new Card(item, ".card-template_type_default");
const cardElement = card.generateCard();
document.body.append(cardElement);
});
Gracias a este pequeño cambio, esta clase es ahora apta para ser escalada. Puedes ver
el código final de la lección en repl.it (materiales en inglés). Qué bien se siente uno cuando
planifica con antelación, ¿verdad? Es como si fuéramos ardillas en septiembre recogiendo
nueces para el invierno. ¡Pasemos a los ejercicios!
Sprint 8
Capítulo 4/10
Interfaces en POO — Parte 1
Controladores de eventos
El director del proyecto ha decidido añadir una funcionalidad que permita a los usuarios
seleccionar sus propios mensajes:
La tarea parece bastante sencilla. Tan solo tenemos que añadir un detector de eventos de
clic al mensaje y hacer que podamos cambiar a la clase activa card__text_is-active. Vamos a
describir todas estas operaciones dentro de nuestra clase.
Nuestro elemento está situado dentro de la propiedad privada this._element. Necesitamos
seleccionar el texto dentro del elemento, así como añadirle una nueva clase. Luego,
crearemos un método llamado _handleMessageClick(). Lo haremos privado ya que no
necesitamos acceder a este método desde fuera.
Copiar códigoJAVASCRIPT
class Card {
// constructor y otros métodos de la tarjeta
_handleMessageClick() {
this._element.querySelector(".card__text").classList.toggle("card__text_is-active")
}
Cuando conectamos nuestro detector de eventos, utilizamos la función arrow. Si intentas
hacer uso de una función común, this funcionará de forma distinta, así que por ahora, solo
utilizaremos funciones arrow para los callbacks de nuestros detectores. En los siguientes
capítulos profundizaremos en la sintaxis de las funciones y el operador this dentro del
cuerpo de las funciones arrow. En este caso, solo la función arrow permitirá el acceso
a _handleMessageClick() a través de this dentro del cuerpo del detector de eventos.
Vamos a añadir los controladores de eventos dentro de generateCard():
Copiar códigoJAVASCRIPT
generateCard() {
this._element = this._getTemplate();
this._setEventListeners(); // añade los controladores de eventos
this._element.querySelector(".card__avatar").src = this._image;
this._element.querySelector(".card__paragraph").textContent = this._text;
return this._element;
}
El código final tiene el siguiente aspecto:
Copiar códigoJAVASCRIPT
class Card {
constructor(data, cardSelector) {
this._text = data.text;
this._image = data.image;
this._cardSelector = cardSelector;
}
_getTemplate() {
const cardElement = document
.querySelector(this._cardSelector)
.content
.querySelector(".card")
.cloneNode(true);
return cardElement;
}
generateCard() {
this._element = this._getTemplate();
this._setEventListeners();
this._element.querySelector(".card__avatar").src = this._image;
this._element.querySelector(".card__paragraph").textContent = this._text;
return this._element;
}
_setEventListeners() {
this._element.querySelector(".card__text").addEventListener("click", () => {
this._handleMessageClick();
});
}
_handleMessageClick() {
this._element.querySelector(".card__text").classList.toggle("card__text_is-active");
}
}
messageList.forEach((item) => {
const card = new Card(item, ".card-template_type_default");
const cardElement = card.generateCard();
document.body.append(cardElement);
});
Ahora la clase es bastante flexible, asi que podemos manejar fácilmente los eventos de
usuario en cualquier tarjeta. Podemos simplemente crear nuevos métodos según sea
necesario y añadirlos a la lista de detectores dentro de _setEventListeners().
Puedes ver el código final en repl.it (materiales en inglés).
Sprint 8
Capítulo 4/10
Interfaces en POO — Parte 1
Herencia
En la lección anterior, terminamos de hacer las tarjetas de mensajes, y la buena noticia es
que nuestro director de proyecto está satisfecho con el resultado. Sin embargo, no hay
tiempo para descansar porque la interfaz aún no está terminada. Todavía necesitamos que
nuestras tarjetas cambien de aspecto y funcionalidad.
El diseñador nos ha informado de que las tarjetas con los mensajes del usuario deben
colocarse a la derecha y sin avatar, mientras que los mensajes del usuario del otro extremo
deben aparecer a la izquierda junto con su avatar.
Más adelante, habrá aún más diferencias entre estas tarjetas de chat. Pero por ahora,
veamos una maqueta del diseño del chat que pretendemos crear:
_getTemplate() irá a la clase padre. Este método obtiene una plantilla de marcado
utilizando un selector específico. Esto lo va a necesitar cada tarjeta.
_setEventListeners() y _handleMessageClick() irán a la clase padre. Estos métodos asignan
funcionalidad a nuestras tarjetas en función de ciertos eventos. Todas nuestras
tarjetas necesitarán trabajar con eventos.
generateCard() irá a la clase hija. Este método permite llenar las tarjetas de datos y
funcionalidad. En este momento, tenemos dos tipos de tarjetas con datos diferentes,
así que cada una necesitará su propio método.
El código se ve así:
Copiar códigoJAVASCRIPT
class Card {
constructor() {
// vamos a tratar con el constructor un poco más tarde
}
_getTemplate() {
// código de _getTemplate
}
_setEventListeners() {
// código de _setEventListeners
}
_handleMessageClick() {
// código de _handleMessageClick
}
}
generateCard() {
// código de generateCard
}
}
generateCard() {
// código de generateCard
}
}
Parece que el código del método apenas ha sufrido cambios, ¿verdad? Pero en el caso de
que llamemos a los métodos de la clase padre dentro de nuestras clases hijas, tendremos
que escribir super en lugar de this. Esto se debe a que nos estamos dirigiendo a la clase padre
y no a la clase hija.
Por lo tanto, el código del método cambiará. Este es el código del
método generateCard() dentro de la clase hija UserCard:
Copiar códigoJAVASCRIPT
class UserCard extends Card {
constructor() {
// vamos a tratar con el constructor un poco más tarde
}
generateCard() {
this._element = super._getTemplate(); // this reemplazado por super
super._setEventListeners(); // tthis reemplazado por super
this._element.querySelector(".card__paragraph").textContent = this._text;
return this._element;
}
}
// generateCard() va a continuación
}
document.body.append(cardElement);
});
Echemos un vistazo al código final (materiales en inglés) antes de pasar a los ejercicios.
Sprint 8
Capítulo 4/10
Interfaces en POO — Parte 1
Polimorfismo
Cuando se trabaja con el DOM, a veces puede ser útil ampliar las características de sus
elementos dinámicos. Los elementos del mismo tipo tienen un comportamiento específico
acorde con su subtipo. Esto sucede cuando el usuario hace clic en una tarjeta y la diseña de
manera diferente.
El polimorfismo nos ayudará aquí. Vamos a ampliar la funcionalidad de una de las clases
hijas, de modo que esta funcionalidad esté disponible en todas las tarjetas de dicha clase.
Sigamos mejorando nuestra página de chat. En la clase padre Card, el
método _handleMessageClick() activa la clase "card__text_is-active" siempre que el usuario haga
clic en el elemento de clase ".card__text".
Esta clase resalta el texto de la tarjeta con un color diferente:
Copiar códigoJAVASCRIPT
_handleMessageClick() {
this._element.querySelector(".card__text").classList.toggle("card__text_is-active");
}
Nuestro objetivo es hacer que el método _handleMessageClick() de la clase hija UserCard sea más
sólido. Hasta ahora, este método solamente podía añadir una clase adicional al
elemento card__text.
Vamos a añadir una característica más a este método para que pueda cambiar el
atributo class del elemento HTML con la clase card.
El polimorfismo nos permitirá mejorar la funcionalidad de _handleMessageClick() dentro de
nuestra clase hija UserCard. Para ello, en primer lugar tendremos que llamar al método inicial
mediante la palabra clave super. En nuestro caso, llamaremos a super._handleMessageClick(). Una
vez hecho esto, podremos describir la funcionalidad adicional. Veamos algo de código para
que resulte más claro:
Copiar códigoJAVASCRIPT
class Card {
// el constructor y otros métodos
_handleMessageClick() {
this._element.querySelector(".card__text").classList.toggle("card__text_is-active");
}
}
_handleMessageClick() {
super._handleMessageClick(); // llamando al método padre
// agregando una nueva funcionalidad a _handleMessageClick:
// this._element almacena un elemento de la tarjeta
// vamos a añadirle la clase card_is-active
this._element.classList.toggle("card_is-active");
}
}
Hemos implementado la anulación de métodos (overriding). Esto significa que hemos
ampliado la funcionalidad del elemento padre dentro de la clase heredada. Anulación de
métodos (overriding) - recuerda este término, ¡te lo vas a encontrar a menudo!
Puedes encontrar el código final aquí (materiales en inglés).
Este fue un ejemplo muy simple de polimorfismo en acción. En los proyectos reales,
normalmente necesitarás escribir un código de mayor complejidad, y dependerá de ti la
decisión de anular los métodos o reescribirlos completamente.
Sprint 8
Capítulo 4/10
Interfaces en POO — Parte 1
Capítulo 5/10
Personal Branding for the Tech Industry
Tu marca personal es algo que muestras a ciertas personas a través de las entrevistas de trabajo, tu
currículum y las redes sociales. Consiste en cómo te percibe la gente.
El primer paso para desarrollar tu propuesta única de valor y tu marca personal es entender bien tus
puntos fuertes. En otras palabras, ¿qué es lo que hace que TÚ te consideres un profesional?
La creación de esta conciencia te permite descubrir la gama de habilidades y competencias que
aportas:
¿Qué temas te interesan? ¿Qué problemas puedes resolver? ¿Qué oportunidades creas? - Austin
Belcak, Marca Personal (materiales en inglés)
Ejemplos:
Hola, soy Terry. Diseñador de UX, filántropo, empresario y amante de los animales. Me
comprometo a ayudarte a desarrollar una marca personal que se ajuste a tu forma de ser y a tus
necesidades.
Soy Jake, un desarrollador web full stack con una formación STEM en Bioingeniería y mi pasión es
resolver problemas. Mi formación científica me permite utilizar mis habilidades analíticas para
construir productos web responsivos y fáciles de usar.
Soy Sam, un desarrollador web full stack con experiencia en gestión de proyectos. Además de
JavaScript, mi superpoder es la capacidad de comunicación y organización. Soy capaz de crear un
código limpio, pero también hacer que el proceso sea divertido y fluido para todo el equipo.
Puedes encontrar más perfiles
aquí: https://www.indeed.com/career-advice/career-development/personal-brand-statement-
examples (materiales en inglés)
Discurso de presentación
Cuando hayas descubierto tu UVP y tu marca personal, puedes empezar a pensar en tu discurso de
presentación. Un discurso de presentación, también conocido en inglés como elevator pitch, es una
sinopsis rápida de tu trayectoria y experiencia. Los discursos de presentación son una forma
elegante de presentarte que utilizarás en muchas ocasiones durante la búsqueda de empleo, e incluso
después. Por ejemplo, puedes utilizar tu discurso de presentación cuando te pongas en contacto con
otros desarrolladores durante eventos, cuando te presentes a la persona a cargo de la contratación
durante las entrevistas informativas o cuando te pongas en contacto con la empresa de contratación.
Los discursos de presentación se llaman así en inglés (elevator pitch) porque no deberían durar más
que un corto viaje en ascensor (de 30 a 60 segundos). La mejor estructura sería presente-pasado-
futuro: primero, explicas quién eres ahora, luego tu trayectoria y, por último, lo que quieres hacer
por la empresa en el futuro.
Cuando empieces a elaborar tu discurso de presentación, tienes que tener en cuenta algunas cosas:
Mira esta lista de reproducción de vídeos cortos del Taller de Carreras de TripleTen. Estos vídeos te
ayudarán a crear tu propuesta única de valor y tu discurso de presentación. Aprenderás los
conceptos básicos sobre cómo presentarte en público y verás algunos ejemplos en vivo de nuestros
alumnos.
Resumiendo, vamos a descubrir los principales errores del discurso de presentación.
IIFE
Antes de pasar a hablar de los módulos, volvamos al tema del scope de las funciones. Así
estaremos preparados para presentarte un nuevo concepto.
Como sabes, una expresión de función es una función que se declara dentro de una
expresión. Ya estás familiarizado con los casos más comunes en los que podemos utilizar
expresiones de función:
Copiar códigoJAVASCRIPT
// una función almacenada en una variable
const double = function (num) {
return num * 2;
};
(function () {
console.log("¡Hola, mundo!");
});
Sin embargo, una función escrita de esta forma es inútil, ya que simplemente la estamos
declarando aquí sin llamarla ni pasarle nada. Como la función no se utiliza en ningún sitio,
el motor simplemente se deshará de ella. Esto lo podemos arreglar añadiendo algunos
paréntesis al final de nuestro código:
Copiar códigoJAVASCRIPT
(function () {
console.log("¡Hola, mundo!");
})(); // añadamos unos paréntesis al final del código para llamar a la función
Si ejecutamos este código, aparecerá en la consola el mensaje ¡Hola, mundo! La función se
ejecuta en cuanto se declara. Una función llamada y definida con este enfoque se llama
Expresión de Función Inmediatamente Invocada, o con sus siglas en inglés IIFE
(Immediately Invoked Function Expression).
function handleClick(evt) {
// el código del controlador del evento clic
}
button.addEventListener("click", handleClick);
Y aquí está otra vez el código, esta vez con todas las variables locales:
Copiar códigoJAVASCRIPT
(function () {
const button = document.querySelector("button");
function handleClick(evt) {
// el código del handler del evento clic clickbutton
}
button.addEventListener("click", handleClick);
})();
Gracias a nuestro IIFE, no hay más variables globales dentro de nuestro código. Intentemos
pensar en cómo de útil es esto. Algo que puede ocurrir es que si otro desarrollador web
decide declarar una variable button, no afectará a tu código ni al suyo. Tu variable
permanece a salvo dentro del scope de tu IIFE.
Con este método, podemos ocultar nuestras variables del mundo exterior. Los IIFEs son
realmente geniales, ¿verdad? Su introducción contribuyó a la creación de los módulos de
JavaScript, de los que hablaremos en la próxima lección.
Sprint 8
Capítulo 6/10
JavaScript modular
Encapsulación y módulos
Todo este tiempo, hemos estado conectando nuestro código JS a nuestro HTML utilizando
la etiqueta <script>. Pero en realidad, podemos conectar librerías JS enteras de esta misma
manera, <script src="some-script.js">. Esta técnica está bien para una página web pequeña, pero
este enfoque no es práctico para proyectos más grandes que están siendo desarrollados por
un equipo de programadores. En esta situación, el proyecto se vuelve demasiado frágil.
Por ejemplo, si los archivos JS se conectan en el orden incorrecto, puede que la aplicación
pierda completamente su funcionalidad. Por ejemplo, si los archivos JS se conectan en el
orden incorrecto, puede que la aplicación pierda completamente su funcionalidad.
También existe el riesgo de una colisión global de nombres. Las personas que trabajan en
un equipo grande pueden utilizar involuntariamente los mismos nombres en dos variables
distintas y esto puede romper el código.
Básicamente, tenemos dos problemas. Necesitamos separar nuestro código en partes para
que sus funcionalidades no interfieran entre sí. También necesitamos que nuestros scripts
funcionen independientemente del orden en que estén conectados. La organización del
código ayudará a evitar estos riesgos.
Importar a un módulo
Después de exportar nuestras funciones y variables, podemos llevarlas a otro módulo
utilizando la sentencia import. Esta sentencia nos permite acceder a variables almacenadas
en otros archivos:
Copiar códigoJAVASCRIPT
// script-02.js
Exportar al declarar
Puedes exportar todo tipo de datos, incluidas las variables, las funciones y las clases,
cuando son declaradas o se escriben por primera vez:
Copiar códigoJAVASCRIPT
// exportar una variable
export const str = "Este string es un secreto";
export const date = [12, 22, 31];
}
}
// renombrar al exportar
export { array as arr, arrSquared as sq };
Copiar códigoJAVASCRIPT
// index.js
La sentencia import
Al igual que con las exportaciones, puedes importar una o varias exportaciones de un
mismo módulo utilizando llaves { }:
Copiar códigoJAVASCRIPT
// index.js
// para importar varias funciones del archivo data.js, enuméralas entre llaves y sepáralas por una coma
import { array, arrSquared } from "./data.js"
console.log(array); // [1, 2, 3]
console.log(arrSquared); // [1, 4, 9]
En algunos casos, puede ser necesario importar todo el contenido de un módulo, es decir,
todo lo que se puede exportar de él. Para ello, simplemente escribe un asterisco * después
de tu sentencia import, luego escribe as y finalmente un nombre de variable para almacenar el
contenido del módulo. Así es como se ve en el código:
Copiar códigoJAVASCRIPT
// index.js
import * as data from "./data.js";
console.log(data.array); // [1, 2, 3]
console.log(data.arrSquared); // [1, 4, 9]
Sin embargo, no recomendamos esta forma de importar módulos. Este código puede
resultar poco claro y difícil de refactorizar, ya que no siempre sabrás exactamente qué
habrás importado del archivo data.js.
console.log(arr); // [1, 2, 3]
console.log(sq); // [1, 4, 9]
// render-items.js
export default function render() {
// ...
}
// song.js
export default class {
constructor() { }
}
// data.js
export default [12, 22, 31];
Fíjate en que no utilizamos las llaves en las importaciones por defecto.
Copiar códigoJAVASCRIPT
// index.js
renderItems();
Como estamos importando algo mediante una exportación por defecto, debemos
nombrarlos en el momento de la importación:
Copiar códigoJAVASCRIPT
import renderItems from "./render-items.js";
import Song from "./song.js";
import someArr from "./data.js";
Resumen
Sintaxis de export:
Sintaxis de import:
Sprint 8
Capítulo 6/10
JavaScript modular
<script>
console.log(numbers); // error - no existe tal variable en el scope global
console.log(doubledNumbers); // este tampoco se puede encontrar
</script>
Navegadores antiguos
En los navegadores más antiguos, el atributo type="module" no existe. Por lo tanto, si abres
una página web que contenga una etiqueta <script> configurada para módulos, tu JavaScript
no estará habilitado.
Podemos resolver este problema escribiendo un código adicional para los navegadores
antiguos, que asegurará que JavaScript esté habilitado. Solo tenemos que añadir una
etiqueta <script> más y escribir el valor "nomodule" en su type.
Copiar códigoHTML
<!-- este módulo se cargará si el navegador es moderno -->
<script type="module"></script>
<!-- este módulo se cargará si el navegador es antiguo -->
<script type="nomodule"></script>
Sin embargo, en la práctica se utiliza muy poco. Más adelante, estudiaremos herramientas
aún más avanzadas para trabajar con módulos. Entre otras cosas, estas herramientas harán
que nuestro código sea legible incluso cuando se utilicen navegadores antiguos.
<script type="module">
console.log(document.querySelector('input')); // <input>
/* cuando utilizamos módulos, podemos acceder a nuestra entrada sin ningún problema */
</script>
<input type="text">
Dicho esto, siempre incluiremos los scripts al final de <body> para evitar estos problemas
que pueden aparecer.
Sprint 8
Capítulo 6/10
JavaScript modular
Sprint 8
Capítulo 7/10
Trabajo en equipo
Trabajo en equipo
Imagina que has empezado un nuevo trabajo y estás deseando conocer a tus compañeros y
compañeras y formar parte del equipo. Sin embargo, nada más empezar, te encuentras con algunos
problemas, y te surgen muchas preguntas sobre cómo resolverlos.
En lugar de aconsejarte, un/a desarrollador/a senior te dice que leas la documentación. Más tarde, el
equipo de dirección te envía un montón de correcciones con cosas que tienes que cambiar. Después,
alguien de diseño te pregunta: "¿Podrías idear algo para que la página web sea adaptiva?".
Para evitar un sentimiento de frustración e impotencia, exploraremos las funciones de cada uno de
estos miembros del equipo y descubriremos por qué tus compañeros actúan así.
Ten en cuenta que no es tarea del personal de diseño entender todo lo que está relacionado con el
desarrollo y la programación web. Puede que no sepan que la propiedad flex no es compatible con
las versiones más antiguas de IE o el tiempo que puede llevar crear una galería de imágenes
compleja.
Lo importante es discutir todo con calma y ayudarse mutuamente. No importa quién se haya
equivocado mientras el problema se resuelva con éxito.
Resumen
Los miembros de tu equipo tienen sus propias vidas y problemas. Para trabajar con eficacia y
disfrutar de tu trabajo, debes relacionarte con tus compañeros y compañeras y conocer sus
responsabilidades. Esto te ayudará a comunicarte con mayor empatía y facilitará la resolución de
cualquier conflicto u otros posibles problemas.
Aquí tienes una lista de puntos clave que debes seguir para garantizar una buena comunicación con
tu equipo:
Una pregunta rápida. En resumen, lo más importante cuando se trabaja con un nuevo equipo es
hacerse valer.
Sí
En realidad no. Si empiezas a trabajar con esta actitud, no harás muchas amistades. Intenta ser más
empático con los demás. Esto no significa que tengas que pasar por el aro o ir a por todas para
complacer a todo el mundo. Siempre puedes decir que no cuando lo consideres necesario, pero ser
amable y compasivo con los miembros de tu equipo contribuye en gran medida a mejorar la
comunicación y a crear un ambiente agradable y productivo. Esto hará que tu lugar de trabajo sea
un sitio al que te apetezca ir.
Falso
No es la respuesta correcta esta vez, ¡pero sigue intentando!Inténtalo nuevamente