Type.new()

NOTA: Este artículo está centrado en un tema que ya comenté en el post anterior, pero de una forma más organizada y centrada, algunos fragmentos están extraídos del post anterior para mayor claridad.

Constructores en Javascript

He hablado antes de la limitación de los constructores javascript y sobre todo de la complejidad de extenderlos

function Person(name) {
  this.name = name;
}
Person.prototype.methodA = function() { ... };

function Employee(name, position) {
  Person.call(this, name);
  this.position = position;
}
Employee.prototype = Object.create(Person.prototype);
Employee.prototype.methodB = function() { ... };

Esto es un asunto que trae de cabeza a la mayoría de la gente que programa javascript, la dificiltad que conlleva crear una simple «clase» hasta el punto que en la siguiente versión del ECMAScript (el estándar en el que está basado Javascript) han incluído una forma más sencilla de hacer lo mismo: la palabra clave class

class Person {
  constructor(name) {
    this.name = name;
  }
  methodA() { }
}

class Employee extends Person {
  constructor(name, position) {
    super(name);
    this.position = position;
  }
  methodB() { }
}

Pruébame

Aunque he visto a mucha gente emocionada pensando que ECMAScript 6 traerá clases reales tengo que decir que este código no hace ni más ni menos que lo que hace el primer código. Y es muy importante saberlo porque aunque prezcan classes como las de Java o C++, en este caso siguen siendo objetos usando herencia por prototipos y esconderlo solo servirá para no saber porqué el código no funciona como esperamos.

En cualquier caso vemos que definir tipos en javascript es complicado y la solución propuesta por el equipo de ECMA no es, en mi opinión, la más adecuada.

 Orientado a objetos

Creo que el problema se aloja en la definición que dimos en un principio a «Programación Orientada a Objetos» (Object Oriented Programming, OOP) ya que los primeros lenguajes OOP creaban objetos usando clases y otras herramientas, y aunque los objetos son la base del sistema la estructura está dada por las clases. Lo que sería Programación Orientada a Objetos con Clases.

Después recibimos otros lenguajes que también se definian como «Programación Orientados a Objetos» pero enfocado de otra forma, entre ellos javascript. En este caso el lenguaje no tiene clases sino que todo son objetos y la estructura se crea mediante prototipos, todo objeto puede ser el prototipo de otro objeto y esto significa que si B prototipa a A todas las propiedades que A tenga también existirán en B. Esto es lo que llamo Programación Orientada a Objetos con Prototipos.

Me he cruzado con mucha gente que piensa que la Programación Orientada a Objetos no es posible sin clases y que si Javascript no tiene clases no puede denominarse orientado a objetos. Como en todo debate entre geeks acabamos en la wikipedia:

La programación orientada a objetos o POO (OOP según sus siglas en inglés) es un paradigma de programación que usa los objetos en sus interacciones, para diseñar aplicaciones y programas informáticos. Está basado en varias técnicas, incluyendo herencia, cohesión, abstracción, polimorfismo, acoplamiento y encapsulamiento.

En resumen, un lenguaje orientado a objetos es el que tiene objetos (brillante conclusión) y cumple una serie de técnicas (herencia, cohesión, abstracción, polimorfismo…) que en el caso de Java se hace mediante clases y en el caso de Javascript se hace mediante prototipos.

 Los inicios de Javascript

Javascript en sus inicios se llamó LiveScript, cuenta la leyenda que por aquella época Java estaba teniendo mucho éxito y por marketing se decidió llamar al nuevo lenguaje JavaScript. También cuenta que por el mismo motivo a última hora se decidió modificar el lenguaje para parecerse más a Java añadiendo, entre otras funcionalidades, el operador new para que pareciera tener clases.

Hay algo muy curioso en los constructores Javascript, que en el fondo son simples funciones, y es que todas las funciones javascript tienen la propiedad prototype que por defecto trae un objeto que solo tiene una propiedad, la propiedad constructor que es el propio constructor.

function Testing() { }
console.log(Testing.prototype.constructor === Testing);

var proto = Testing.prototype;
console.log(proto.constructor.prototype === proto);

 Constructores vs objetos prototipo

Esto me hace pensar que quizás la intención original de los objetos en javascript no era tener constructores que contienen prototipos sino tener prototipos que contienen constructores. Es decir: en lugar de…

function MyType() {
  this.id = 1;
}
MyType.prototype.methodA = function() { ... }

Hacer esto…

var MyType = {
  constructor: function() {
    this.id = 1;
  },
  methodA: function() { ... },
};

Vaya! No parece una forma mucho más sencilla de declarar tipos? [Aquí][5] podemos comparar el mismo tipo escrito con constructores y con este paradigma y juzguen ustedes mismos. Y que pasa cuando intentamos invocar al constructor? hay que usar .call() o .apply() para pasarle this?

var isntancia = Object.create(MyType);
instancia.constructor();

BOOM! Constructor ya recibe this porque es invocado directamente en la instancia! No es exageradamente sencillo y lógico desde éste punto de vista?

Además por accidente hemos quitado de en medio la función constructora y lo que tenemos es un simple objeto, el elemento más básico de la programación orientada a objetos. Es decir, para declarar un tipo solo tenemos que crear un objeto, para prototipar un objeto solo necesitamos un paso

var SubType = Object.create(MyType);

No estamos obligados, a diferenia del primer caso, a crear un nuevo constructor para crear un subtipo, por la herencia por prototipos tenemos el mismo constructor que MyType

console.log(SubType.constructor === MyType.constructor);
// true

Pruébame

Y la mejor parte, que pasa si queremos crear un tipo sin constructor? No hay problema.

var MyType = {};
console.log(MyType.constructor); // Object

Pruébame

ECMAScript 6

Este paradigma se parece bastante a la forma de crear clases en ECMAScript 6

class MyType {
  constructor() {
    this.id = 1;
  }
  methodA() { ... }
}

Que alguno dirá, si, pero con las clases de ECMAScript 6 podemos extender clases, llamar al método padre con super y nos ahorramos poner function… Pero esas no son funcionalidades de las clases de ECMAScript 6, esas son funcionalidades de todos los objetos en ECMAScript 6.

// Clase ECMAScript 6
class Employee extends Person {
  constructor(name, postition) {
    super(name);
    this.position = postition;
  }
  methodA() { ... }
}

// Objeto en ECMAScript 6 estándar
var Employee = {
  __proto__: Person,

  constructor() {
    super(name)
    this.id = 1;
  },
  methodA() { ... }
};

Las diferencias entre una clase ECMAScript 6 y un objeto en ECMAScript 6 son mínimas, pero mientras que una clase nos hace pensar que Employee se comportará como una clase Java cuando no es así, un objeto es simplemente eso, un objeto y todos somos capaces de entender como se comporta un objeto, no? (si no que haces leyendo esto? o.o)

Pero dejemos ECMAScript 6 de lado por ahora, que aún tiene que transcurrir tiempo antes de que podamos usarlo en serio.

Instanciación

Hasta aquí era la definicion del tipo, pero como creamos una instancia? Primero tendríamos que plantearnos que es una instancia, si no tenemos clases podemos tener instancias? Según la wikipedia

En el paradigma de la orientación a objetos, una instancia (en inglés, instance) se refiere a una realización específica de una clase o prototipo determinados.

Pero, al menos a mi, no importa mucho la palabra; el tema es que nosotros creamos objetos para que hagan de prototipos y queremos crear «instancias» de estos prototipos. La forma de prototipar un objeto es usando Object.create()

var instance = Object.create(MyType);

Pero, un momento… Esto es exactamente lo mismo que hicimos para crear un subtipo, no? Si. Entonces en que se diferencia una instancia de un subtipo? En general, nada. Una instancia ES un subtipo. Pero en la mayoría de los casos las «instancias» tienen una necesidad que los subtipos no tienen: en una instancia se invoca al constructor, en un subtipo no.

var MyType = {
  constructor: function() {
    this.id = 1;
  }
};

// Crear sub-tipo
var SubType = Object.create(MyType);

// Crear instancia
var instance = Object.create(MyType);
instance.constructor();

Esta similitud entre una instancia y un SubTipo nos ayuda a entender hasta que punto en el fondo Javascript es muy, muy sencillo: todo son objetos; no hay diferencia entre un tipo y una instancia porque la diferencia es conceptual.

Esto es muy útil para entender la sencillez y el corazón de Javascript, pero es un poco tedioso tener que hacer dos pasos para instanciar, podríamos simplificarlo?

Type.new() es el nuevo new

Lo cierto es que podríamos, podemos hacer una función que haga este proceso:

function createInstance(Type) {
  var instance = Object.create(Type);
  instance.constructor();
  return instance;
}

var Type = {
  constructor: function() {
    this.id = 1;
  }
};

var instance1 = createInstance(Type);

var TypeWithoutConstructor = {};
var instance2 = createInstance(TypeWithoutConstructor);

Pruébame

Y que pasa si en lugar de llamarla createInstance la llamamos $new por ejemplo?

var instance = $new(MyType);

Empieza a parecer similar, solo nos faltaría cambiar la funcion $new para pasarle parámetros al constructor

function $new(Type, params) {
  var instance = Object.create(Type);
  instance.constructor.apply(instance, params);
  return instance;
}

var Type = {
  constructor: function(name) {
    this.name = name;
  }
};

var instance = $new(Type, [ 'bob' ]);

Pruébame

Parece funcionar, pero solo para acabar de pulirlo, porqué no ponemos $new como método de Type? así podríamos pasarle los argumentos sin el array y como ECMAScript 5 nos permite usar palabras clave como propiedades de objeto podemos llamarlo simplemente new.

var Type = {
    new: function() {
        var instance = Object.create(this);
        instance.constructor.apply(instance, arguments);
        return instance;
    },
    constructor: function(name) {
        this.name = name;
    }
};

var instance = Type.new('bob' );

Pruébame

Y tenemos una forma que podemos usar con ECMAScript 5 para crear tipos e instancias de forma sencilla. Pero que diferencia hay entre esto y hacer un new? A parte de la ya mencionada simplicidad para crear y extender tipos, tiene más ventajas, principalmente porque nos permite controlar más exactamente cómo se crea un objeto que en algunos casos es conveniente cambiarlo (en la mayoría no), pero excede el alcance de éste post.

 Conclusión

Después de pasarme los últimos años probando mil y una formas de crear y extender «clases» para encontrar la forma más sencilla, rápida y elegante, muchas de ellas registradas en este blog; me quedo con ésta. La lección que me dio javascript es que no es conveniente luchar contra su naturaleza, si queremos usar javascript y no morir en el intento lo más razonable es usar javascript y no tratarlo en contra de su naturaleza.

Aún está por verse pero creo que este sistema incluso puede competir cara a cara con las «clases» de ECMAScript 6, pero en cualquier caso la conversión entre un tipo creado por constructor y uno creado con este sistema es muy sencilla

Por ejemplo, convertir un tipo creado con este sistema a constructor para usarlo con new:

var MyType = {
  myMethod: function() { ... },
};

function MyConstructor() {
  MyType.constructor.apply(this, arguments);
}
MyConstructor.prototype = MyType;

Pruébame

O convertir un constructor a este paradigma:

function MyConstructor() {
  this.value = 1;
}
MyConstructor.prototype.myMethod = function() { ... };

var MyType = MyConstructor.prototype;
// y si queres añadir new...
MyType.new = $new;

Pruébame

Inicializador

Para finalizar un bonus, después de toda esta travesía me he dado cuenta que el constructor, que para javascript parece tan importante, no lo es tanto. Si nos paramos a mirar el constructor vemos que es una simple función

function MyType() { ... }

No tiene nada de especial, incluso podemos invocarla como una función y no construye nada. Entonces quién construye? new. Es el operador new el que crea el nuevo objeto y luego invoca el método llamado «constructor», que no se diferencia en nada de cualquier otro método que podría tener el objeto.

Por como yo lo veo, la función del constructor es más inicializar que construir, debe encargarse de inicializar las propiedades del objeto, no construir. Visto así es evidente que el nombre «constructor» no es apropiado, en mi caso prefiero la denominación «initializer» o simplemente «init», como Backbone ya hace en sus objetos.

Por eso en mis proyectos cuando utilizo este paradigma, prefiero que mi función $new invoque el método init en lugar de llamar al método constructor.

function $new() {
  var obj = Object.create(this);
  obj.init.apply(obj, arguments);
  return obj;
}

var MyType = {
  new: $new,
  init: function() {
    this.value = 1;
  }
};

var instance = MyType.new();

Pruébame

16 thoughts on “Type.new()

  1. When I originally commented I clicked the «Notify me when new comments are added» checkbox and now each time a comment is added I get four emails with the same comment. Is there any way you can remove me from that service? Thank you!

  2. Hi there terrific website! Does running a blog similar to this take a lot of work? I have virtually no understanding of coding however I had been hoping to start my own blog in the near future. Anyway, should you have any ideas or tips for new blog owners please share. I know this is off subject however I simply wanted to ask. Kudos!

  3. Hi there! I just wanted to ask if you ever have any issues with hackers? My last blog (wordpress) was hacked and I ended up losing several weeks of hard work due to no backup. Do you have any methods to stop hackers?

  4. The other day, while I was at work, my sister stole my iPad and tested to see if it can survive a 40 foot drop, just so she can be a youtube sensation. My apple ipad is now broken and she has 83 views. I know this is totally off topic but I had to share it with someone!

  5. Hi there would you mind letting me know which web host you’re utilizing? I’ve loaded your blog in 3 different web browsers and I must say this blog loads a lot faster then most. Can you suggest a good hosting provider at a honest price? Thanks a lot, I appreciate it!

  6. Hey there! I understand this is kind of off-topic however I needed to ask. Does running a well-established website such as yours take a lot of work? I am brand new to running a blog but I do write in my diary everyday. I’d like to start a blog so I can share my personal experience and thoughts online. Please let me know if you have any kind of ideas or tips for brand new aspiring blog owners. Appreciate it!

  7. I know this if off topic but I’m looking into starting my own weblog and was curious what all is required to get setup? I’m assuming having a blog like yours would cost a pretty penny? I’m not very internet smart so I’m not 100% certain. Any tips or advice would be greatly appreciated. Many thanks

  8. Have you ever considered publishing an e-book or guest authoring on other blogs? I have a blog based on the same topics you discuss and would love to have you share some stories/information. I know my audience would value your work. If you are even remotely interested, feel free to send me an e mail.

  9. Awesome blog! Do you have any suggestions for aspiring writers? I’m hoping to start my own website soon but I’m a little lost on everything. Would you suggest starting with a free platform like WordPress or go for a paid option? There are so many choices out there that I’m totally confused .. Any recommendations? Bless you!

  10. My spouse and I absolutely love your blog and find almost all of your post’s to be
    just what I’m looking for. Would you offer guest writers to
    write content in your case? I wouldn’t mind producing
    a post or elaborating on some of the subjects you write about here.
    Again, awesome website!

  11. Hi there, I discovered your website by means of Google at the same time as looking for a related topic, your site got here up, it appears to be like good. I have bookmarked it in my google bookmarks.

  12. I intended to put you a tiny word in order to thank you as before for the wonderful information you’ve discussed in this article. It’s particularly open-handed of people like you to offer freely what a number of us would have marketed as an e book to get some cash on their own, particularly now that you could possibly have tried it if you considered necessary. The things additionally worked like a good way to fully grasp other individuals have similar fervor just like my own to realize many more on the subject of this condition. I think there are many more pleasant moments ahead for people who look over your website.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *