Trabajando con {objetos} en Javascript

Cuando iniciamos en el mundo de javascript (JS o ES)nos encontramos con muchas cosas raras, mejor dicho desconocidas para nosotros, especialmente cuando interactuamos con los objetos, cuántas veces nos preguntamos la funcion o significado de palabras como this, new, class entre otras, sin entender su verdadera funcion y teniendo dudas ¿de qué está haciendo?, ¿por qué funciona?. El leguaje ha crecido en un corto periodo de tiempo y la forma de implementar objetos ha cambiado a lo largo de su historia (mayormente influenciado por el uso de “transpiladores”) pero siempre debemos tener claro todo en JS son objetos, esto suena difícil de entender al principio, pero, al final del día estamos creando objetos que se conectan con otros objetos, incluso heredan de otros objetos. Hoy aprenderemos como usarlos y por que todo es un objeto en javascript.

Comenzando desde la programación orientada a objetos (POO) tendemos a cree que un objeto debe ser algo complejo y que son instancias de una clase que define su comportamiento basado en sus métodos y atributos, además de tener la ventaja de encapsulan logica, compartir codigo gracias a la herecia, entre varias cosas mas, en cambio, en el mundo de JS esta definicion puede costarnos un poco mas de trabajo, ya que tenemos colecciones de datos llamados objetos (y alguno tipos de dato primitivos), y no vienen de una instancia directa de una clase si no que se crean de una forma mas sencilla haciendo uso de funciones constructoras como lo veras mas adelante.

¿Qué son los objetos?

Para entenderlos mejor veamos la definicion que Mozilla Developer Network (MDN) tiene:

An object is a collection of related data and/or functionality (which usually consists of several variables and functions — which are called properties and methods when they are inside objects.)

Que en otras palabras, un objeto o mapa es una colección de valores que accedemos a sus propiedades por una referencia que llamamos llave, con los objetos podemos crear estructuras de datos, los valores no se restringen a ningún tipo pueden ser números, cadenas de texto, arreglos, funciones y otros objetos anidados, por ejemplo:

var person = {
  name: ['Bob', 'Smith'],
  age: 32,
  gender: 'male',
  bio: function() {
    alert(this.name.join(' ') + ' is ' + this.age + ' years old.');
  },
  greeting: function() {
    alert('Hi! I\'m ' + this.name[0] + '.');
  }
};

Para interactuar con el objeto del ejemplo anterior, podemos hacerlo de las siguientes maneras:

  • Dot Notation (Notación de punto): Es a través del uso de puntos entre el objeto y sus propiedades, ejemplo: object.keyName.

  • Bracket Notation: (Notación de corchetes): Parecido a como manipulamos arreglos, haremos uso de corchetes pasandole el string de la propiedad que queremos, es especialmente util cuando trabajamos con llaves dinamicas, el ejemplo: object[‘keyName’]

  • Getter and Setters: Son metodos que nos ayudan a obtener y asignar valores, a modo de funciones de ayuda que nosotros definiremos y que actuarán de intermediarias para comunicarnos con las propiedades de nuestro objeto, ejemplo:

    const profile = {
      name: '',
      surname: '',
      friend: [1, 2, 3, 4, 5],
      age: 19,
      set fullname (fullname) {
        // fullname could be 'John Jackson'
        this.name = fullname.split(' ')[0]
        this.surname = fullname.split(' ')[1]
      },
      get fullname () {
        return 'my name is ' + this.name + ' ' + this.surname
      }
    }
    
    profile.fullname = 'John Jackson'
    profile.name // John
    profile.fullname // 'John Jackson'
    

El objeto: Object

Es la especificacion más importante en core de JS, está define cómo se crean, comportan y manipulan los objetos. Por ejemplo en la creacion de objetos, podemos hacer uso del metodo Object.create(), o su variente en forma literal {key: value} que también hace uso Object.create.

Tambien nos provee de otros métodos que nos facilitan manejar objetos como son:

  • Object.keys() para acceder a las llaves en un array.
  • Object.assign() usado bajo el agua cuando hacemos spread syntax {...otherObject}.
  • Object.entries() permite pasar una función como callback y dentro podemos acceder al key y value de la colección, util para recorrer objetos.
  • Object.values() para obtener los valores en un array, bueno estos algunos de los mas usados.

Object.create

Hasta el momento nos hemos limitado a conocer la definicion de un objeto asi como las formas de interactuar con uno, pero muy poco de como construirlos. Como lo mencione en el punto anterior, con Object.create podemos crear objetos a partir de otro inicial, es decir el objeto provisto como parámetro servirá como prototipo del objeto resultante, con ello podemos hacer uso de sus propiedades sin que esten definidas directamente en el objeto devuelto, ejemplo:

const profile = {
  name: '',
  surname: '',
  set fullname (fullname) {
    // fullname could be 'John Jackson'
    this.name = fullname.split(' ')[0]
    this.surname = fullname.split(' ')[1]
  },
  get fullname () {
    return 'my name is ' + this.name + ' ' + this.surname
  }
}

const myprofile = Object.create(profile)
profile.fullname = 'Peter Lion'
profile.fullname //my name is peter Lion
profile.name //Peter

Podemos notar que myprofile puede hacer uso de todas las propiedades que tiene profile, asi es como funciona la herencia en JS, internamente se esta ligando el objeto profile dentro del prototipo de myprofile, de forma simple es un objeto que se conecta a otro y puede acceder a las propiedades del objeto que se referencia en su propiedad __proto__.

Nota: Todos los objeto creados seran afectados si su prototipo es alterado.

Función constructora

JS fue muy influenciado por Java ya que era el lenguaje de moda, para hacerlo mas amigable a los developers se creo una sintaxis para crear objetos similar a como se hace en Java, con el uso de funciones especiales que llamamos contructor, un ejemplo es:

function Person(name, age) {
  this.name = name
  this.age = age
}

Person.prototype = {
  get getname() {
    return this.name
  },
  set setname(name) {
    this.name = name
  }
}

const myself = new Person('Gibran', 26)

myself.getname() // Gibran
myself.setName('Gibran Lopez')
myself.getname() // Gibran Lopez

En el codigo anterior podemos notar dos palabras reservadas como new y this que tambien existen en Java, pero, ¿Qué diferencia tienen estas funciones constructuras sobre otros lenguajes orientados a objetos (POO)? Para verlo mas claramente usemos un ejemplo un poco mas complejo donde se aprecie la herencia en javascript:

function Person(name, age) {
  this.name = name
  this.age = age
}

Person.prototype = {
  get getname() {
    return this.name
  },
  set setname(name) {
    this.name = name
  }
}

function Profile(career, expertise) {
  this.career = career
  this.expertise = expertise
}

const personExtended = new Person('', 0)

Profile.prototype = Object.assign(personExtended, {
  get getcareer() {
    return this.career
  },
  set setcareer(career) {
    this.career = career
  }
})
Profile.prototype.constructor = Profile

const myprofile = new Profile('', 20)

myprofile.setname = 'Peter'
myprofile.name // Peter
myprofile.setcareer = 'Engenieer'
myprofile.career // Engenieer
myprofile.expertise // 20

Resaltando las diferencias del ejemplo anterior, tenemos que:

  • En JS no se puede encapsular atributos/métodos, es decir todas nuestras propiedades son publicas.
  • El operador this es una referencia al objeto que se va a crear, del cual solo podemos acceder a los atributos que declaremos en en el constructor.
  • Debemos declarar los métodos de instancia en el prototipo, que nos faciliten acceder a sus propiedades.
  • La herencia funciona de forma distinta, se debe declarar al prototipo de la función constructora con un objeto del cual heredara, a diferencia de Java donde se especifica en la definicion de la clase, ejemplo: class Profile extend Person.
  • Es importante re-asignar la referencia original en el prototipo de la clase hija, en este punto nos damos cuenta que JS no sigue la POO tipicamente como la conocemos, ejemplo: Profile.prototype.constructor = Profile
  • El operador new solo ejecuta la clase constructora, asigna el prototipo al objeto final y lo devuelve
  • Otro punto a destacar es que si el objeto que se uso en la herencia es modificada, los cambios afectan a todos lo objetos que se hayan heredado. ejemplo: si modificamos personExtended afectaremos a todos los objetos Profile.

Estos son algunos puntos a considerar cuando trabajamos con javascript, es importante mencionar que se han creado formas mas sencillas para manejar los objetos a partir de ES6, como veremos a continuacion.

Uso de class

A partir del estándar ES6 se implemento un nuevo operador, llamado class con él vamos a poder crear clases y extenderlas de una forma mas familiar, dejando a los desarrolladores con la sensacion de un lenguaje POO como Java, sin embargo esto pudiera no ser lo que parece, internamente javascript hace por ti el trabajo sucio.

Un ejemplo de la nueva sintaxis es:

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
  // Getter
  get area() {
    return this.calcArea();
  }
  // Method
  calcArea() {
    return this.height * this.width;
  }
}

const square = new Rectangle(10, 10);

console.log(square.area); // 100

Esto ciertamente hara que los programadores esten mas contentos, siendo mucho mas amigable y facil de reconocer, incluso no seria problema el comparar esta nueva clase con otros lenguajes, aun asi, javascript tiene que hacer el manejo interno de tomar al constructor como función constructora, crea un prototipo, asignarle los métodos y devolvernos un objeto, pero es importante saber el uso interno de los prototipos.

Conclusión

Hemos notado muchos cambios en la forma de crear objetos, desde una estructura simple hasta otras dinámicas, incluso las clases parecen una buena opción a simple vista pero en mi carrera como desarrollador me he dado cuenta que esto es muy verboso, la idea de abstraer los objetos es complicada y nos olvidamos que los objetos deben ser estructuras de datos simples y la modificacion de sus estados internos debe ser a través de funciones, asi como preferir la composición sobre la herencia. Tambien es importante saber como funciona JS para programarlo mejor, presentandote estos conceptos claves antes de que te veas forzado a pelear con ellos, y puedas entender lo que esta sucediendo y decidir cual es la mejor forma de resolver tus problemas.



Gibran Lopez Morales

Gibran Lopez Morales

Acerca de mi puedo decirte que soy una persona de muchos gustos, serio, alegre y muy feliz, y un 50% de sociable o más, me gusta compartir/aprender en comunidad, en fin soy desarrollador de software


comments powered by Disqus

Siguenos

Boletí de noticias