🌱 Digital Garden

Search

Search IconIcon to open search

OOP en JavaScript

Last updated Jan 22, 2023 Edit Source

El OOP en JavaScript esta implementado por prototipos, aun asi, sigue los cuatro principios basicos:

Existen tres forma de implementar el OOP basado en prototipos (delegacion) en JS:

# Funcion Constructor

Son funciones normales, la unica diferencia esque para llamar un constructor utilizamos “new”.

1
2
3
4
5
6
const Constructor = function(attribute1, attribute2) {
	this.attribute1 = attribute1;
	this.attribute2 = attribute2;
}

const object = new Constructor('Algo', 'Atributo');

Al utilizar esta forma ocurre lo siguiente:

  1. Se crea un nuevo objeto
  2. Se llama a la funcion constructora(con parametros y todo)
  3. La keyword “this” se asigna a este objeto creado en el contexto de la funcion
  4. El objeto se vincula a un prototipo
  5. El objeto se retorna de la funcion

# Prototipos

Para resumir, una funcion (TODAS) siempre tienen una propiedad llamada “prototype”, incluidas las funciones constructoras. Por defecto, una funcion constructora dara acceso a todos los atributos y metodos definidos en el prototipo a sus instancias.

1
2
3
4
5
6
7
8
Person.prototype // Propiedad prototype de la funcion constructora Person.

Person.prototype.function = function () {
return 'Este es el resultado de un metodo';
}

const jaime = new Person();
console.log(jaime.function()); //

Haciendolo de esta forma, todos los objetos que sean instanciados mediante la funcion constructora “Person” acceden a la funcion del prototipo. Asi mismo, cada uno de los objetos puede utilizar sus atributos dentro de los prototipos.

# Prototype Chain

Una serie de uniones entre objetos y sus prototipos. Esta termina cuando se llega a Object.prototype, el cual es null.

Como dato curioso la sintaxis de corchetes {} es un shorthand para crear un objecto con la funcion constructora “Object”, es decir, los siguientes son equivalentes:

1
2
const obj1 = {};
const obj2 = new Object();

# Clases de ES6

Las clases en JavaScript no trabajan como en otros lenguajes, unicamente son sintaxis que utiliza los mecanismos anteriores para trabajar.

Es bastante parecida a la sintaxis de Java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class ClassName {
	constructor(attribute1, attribute2) {
		this.attribute1 = attribute1;
		this.attribute2 = attribute2;
	}

	// Los metodos son agregados por defecto al prototipo de esta clase. En este caso ClassName.prototype
	print() {
		console.log(attribute1)
	}
}

# Setters y Getters

Son dos funciones especiales que podemos declarar en nuestros objetos en JavaScript. Son funciones que para el mundo externo les parecen como si fueran propiedades.

1
2
3
4
5
6
7
get attribute1() {
	return this.attribute1;
}

set attribute1(attribute1) {
	this.attribute1 = attribute1;
}

Para acceder a ellos, unicamente se leen como una propiedad

1
console.log(ClassName.attribute1);

Finalmente, estas tambien pueden ser utilizadas dentro de nuestras clases

1
2
3
get attribute3() {
	return this.attribute1 + this.attribute2;
}

Error si tienes el mismo atributo en el constructor y el setter. Se reventara el callstack debido a que se llamara en un loop infinito el uno a la otra. Para corregir esto la convencion es utilizar una nueva propiedad dentro del setter:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Person {
	constructor(fullName) {
		this.fullName = fullName
	}

	set fullName(fullName) {
		this._fullName = fullName;
	}

	get fullName() {
		return this._fullName;
	}
}

# Metodos estaticos

Son metodos que se encuentran unidos escencialmente a una clase, en el caso de JavaScript, se encuentran como parte de la funcion constructora.

Un ejemplo de estas son las siguientes funciones:

1
2
Array.from(iterable)
Number.parseFloat(floatNumber)

Recordando que todo son objetos en JavaScript, puedes añadirle metodos a una funcion constructora

1
2
3
Person.method = function() {
	// code
}

Y estos a su vez no seran heredados debido a que no se encuentran en el prototipo!.

En el caso de las clases de ES6 tenemos la sintaxis especial de ‘static’

1
2
3
4
5
class ClassName {
	static method() {
		// code
	}
}

# Object.create

Esta es la tercera forma de implementar OOP en JavaScript.

Se conserva la idea de los prototipos pero sin la presencia de las propiedades, mediante esta, podemos crear un object literal que sirva como prototipo para todos y posteriormente asignarlo como prototipo usando la funcion Object.create.

1
2
3
4
5
6
7
8
const PersonPrototype = {

	function() {
		// code
	}
}

const steven = Object.create(PersonPrototype) // Recibe el prototipo para un objeto

La diferencia con las funciones constructoras esque, como se ve, en este caso estamos asignando de manera manual el prototipo del objeto. Caso contrario a las funciones constructoras donde estas lo hacen de forma automatica.

# Herencia

Para establecer una tipica herencia de la OOP en JavaScript debemos utilizar el asignador de prototipos (Object.create). Mediante este, podemos asignar el prototipo de un objeto.

Para lograr la herencia, debemos hacer que el prototipo de la funcion constructora de la subclase apunte al prototipo de la funcion constructora de la superclase.

1
2
3
4
const Person = function () {}
const Student = function() {}

Student.prototype = Object.create(Person.prototype); 

Finalmente, algo que debemos hacer es corregir el side effect que tiene Object.create, y esque esta funcion actualiza el constructor del prototipo de la subclase para que sea el constructor de la superclase, para corregirlo:

1
Student.prototype.constructor = Student

Con esto, debemos tener el Prototype Chain bien asignado.

Finalmente, para crear una instancia primero se debe llamar el constructor de la superclase para que se coloquen las propiedades correctamente. Para esto, utilizaremos el metodo especial .call del prototipo funcion que nos permite asignar ’this’ dentro del scope de cada funcion:

1
2
3
4
5
6
const Student = function() {
	Person.call(this, 'Nombre X');
	this.arg1 = 0;
}

const Pepe = new Student();

Como conclusion, el prototype chain nos sirve para establecer la cadena de herencias en JavaScript.

# Herencia entre Clases Sintaxis ES6

Existe una forma mucho mas parecida a Java y mucho mas facil para implementar la herencai entre clases, esta utiliza de las keywords “extends” y la funcion “super()”.

1
2
3
4
5
6
7
8
9
class Person {
	// code
}

class Student extends Person {
	constructor () {
		super();
	}
}

Con solo esto, todo el prototype chain, y funciones constructoras se coloca de forma adecuada.

# Herencia con Object.create

Para hacer una herencia entre los prototipos de los objetos basta con introducir una nueva clase a la Prototype Chain

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const PersonProto = {
	// code
}

const StudentProto = Object.create(PersonProto)

StudentProto.init() = function () {
	// code
}

const studentInstance = Object.create(StudentProto);

Podemos aprovechar nuevamente “call” para llamar los metodos del Prototipo de Person con “this” actualizada

1
2
3
4
StudenProto.init() = function () {
	PersonProto.init.call(this); // Ejecuta el metodo de PersonProto
	// Otro codigo
}

# Encapsulamiento

En JavaScript podemos implementar un tipo de encapsulamiento a nuestros atributos:

La sintaxis en JavaScript es utilizar ‘#’:

1
2
3
4
5
6
class SomeClass {
	#field
	constructor (field) {
		this.#field = field;
	}
}

# Resumen