Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 

#Herencia

##Encadenamiento de Prototipos (Prototype Chaining)

Es el mecanismo por defecto que describe el standard ECMA para implementar la herencia en Javascript

function Shape(){
  this.name = 'shape';
  this.toString = function() {return this.name;};
}

function TwoDShape(){
  this.name = '2D shape';
}

function Triangle(side, height) {
  this.name = 'Triangle';
  this.side = side;
  this.height = height;
  this.getArea = function(){
    return this.side * this.height / 2;
  };
}

TwoDShape.prototype = new Shape();
Triangle.prototype = new TwoDShape();

TwoDShape.prototype.constructor = TwoDShape;
Triangle.prototype.constructor = Triangle;

>>> var myTriangle = new Triangle(5,10)

>>> myTriangle.getArea() === 25
true
>>> myTriangle.hasOwnProperty("getArea")
true

>>> myTriangle.toString() === "Triangle"
true
>>> myTriangle.hasOwnProperty("toString")
false

>>> myTriangle.constructor === Triangle
true

>>> myTriangle instanceof Shape
true
>>> myTriangle instanceof TwoDShape
true
>>> myTriangle instanceof Triangle
true

>>> Shape.prototype.isPrototypeOf(myTriangle)
true
>>> TwoDShape.prototype.isPrototypeOf(myTriangle)
true
>>> Triangle.prototype.isPrototypeOf(myTriangle)
true

>>> String.prototype.isPrototypeOf(myTriangle)
false

Encadenamiento de prototipos

>>> TwoDShape.prototype.hasOwnProperty("toString")
true
>>> Shape.prototype.hasOwnProperty("toString")
false
>>> myTriangle.hasOwnProperty("getArea")
true
>>> Triangle.prototype.hasOwnProperty("getArea")
false

>>> var td = new TwoDShape();

>>> td.constructor === TwoDShape
true
>>> td.toString() === "2D shape"
true

>>> var s = new Shape()

>>> s.constructor === Shape
true
>>> s.toString() === "shape"
true

Se recomienda mover a los prototipos todas las propiedades/métodos reutilizables, y dejar las no-­reutilizables como propias de las instancias


##Moviendo metodos re-utilizables al prototypo

function Shape(){}

// augment prototype
Shape.prototype.name = 'shape';
Shape.prototype.toString = function() {
  return this.name;
};

function TwoDShape(){}

// take care of inheritance
TwoDShape.prototype = new Shape();
TwoDShape.prototype.constructor = TwoDShape;

// augment prototype
TwoDShape.prototype.name = '2D shape';

function Triangle(side, height) {
  this.side = side;
  this.height = height;
}

// take care of inheritance
Triangle.prototype = new TwoDShape();
Triangle.prototype.constructor = Triangle;

// augment prototype
Triangle.prototype.name = 'Triangle';
Triangle.prototype.getArea = function(){
  return this.side * this.height / 2;
};

var myTriangle = new Triangle(5,10)

Moving shared methods

>>> TwoDShape.prototype.hasOwnProperty("toString")
false
>>> Shape.prototype.hasOwnProperty("toString")
true
>>> myTriangle.hasOwnProperty("getArea")
false
>>> Triangle.prototype.hasOwnProperty("getArea")

##Herencia sólo del prototipo

Es un mecanismo más eficiente ya que no se crean nuevas instancias sólo para implementar la herencia

La búsqueda por la cadena de prototipos es más rápida (ya que no hay cadena de prototipos, todos los prototipos apuntan al mismo objeto )

function Shape(){}

// augment prototype
Shape.prototype.name = 'shape';
Shape.prototype.toString = function() {
  return this.name;
};

function TwoDShape(){}

// take care of inheritance
TwoDShape.prototype = Shape.prototype;
TwoDShape.prototype.constructor = TwoDShape;

// augment prototype
TwoDShape.prototype.name = '2D shape';

function Triangle(side, height) {
  this.side = side;
  this.height = height;
}

// take care of inheritance
Triangle.prototype = TwoDShape.prototype;
Triangle.prototype.constructor = Triangle;

// augment prototype
Triangle.prototype.name = 'Triangle';
Triangle.prototype.getArea = function(){return this.side * this.height /
2;}

Herencia solo del prototipo

>>> Triangle.prototype.hasOwnProperty("getArea")
true
>>> Shape.prototype.hasOwnProperty("getArea")
true
>>> Shape.prototype.hasOwnProperty("toString")
true
>>> Triangle.prototype.hasOwnProperty("toString")
true

El problema de este método es que al apuntar todos los prototipos al mismo objeto, cuando modificamos alguno de los prototipos, los modificamos todos.

>>> Triangle.prototype.name = 'Triangle';
>>> var s = new Shape()
>>> s.name
"Triangle"

##Función constructora temporal F()

Para solucionar esto podemos usar una función constructora temporal F() vacia y asignarle a su prototype el prototipo de la función constructora padre

De esta manera podemos hacer new F() y crear objetos que no tengan propiedades por si mismos pero que hereden todo del prototype del padre

function Shape(){}

// augment prototype
Shape.prototype.name = 'shape';
Shape.prototype.toString = function() {return this.name;};

function TwoDShape(){}

// take care of inheritance
var F = function(){};
F.prototype = Shape.prototype;
TwoDShape.prototype = new F();
TwoDShape.prototype.constructor = TwoDShape;

// augment prototype
TwoDShape.prototype.name = '2D shape';

function Triangle(side, height) {
  this.side = side;
  this.height = height;
}

// take care of inheritance
var F = function(){};
F.prototype = TwoDShape.prototype;
Triangle.prototype = new F();
Triangle.prototype.constructor = Triangle;

// augment prototype
Triangle.prototype.name = 'Triangle';
Triangle.prototype.getArea = function(){return this.side * this.height /
2;};

>>> var myTriangle = new Triangle(5, 10);
>>> myTriangle.getArea() === 25
true
>>> myTriangle.toString() === "Triangle"
true

>>> var myShape = new Shape()
>>> myShape.name === "shape"
True

>>> Triangle.prototype.name = "super-Triangle"
>>> myTriangle.name === "super-Triangle"
true
>>> myShape.name === "shape"
true

Funcion Intermedia F()

>>> myTriangle.__proto__.__proto__.__proto__.constructor === Shape
true
>>> myTriangle.__proto__.__proto__.constructor === TwoDShape
true

>>> myTriangle.__proto__.constructor === Triangle
true

##Encapsulando la herencia en una función

Podemos encapsular este código en una función extend que nos facilite implementar la herencia

function extend(Child, Parent) {
  var F = function(){};
  F.prototype = Parent.prototype;
  Child.prototype = new F();
  Child.prototype.constructor = Child;
}

function Shape(){};
// augment prototype
Shape.prototype.name = 'shape';
Shape.prototype.toString = function() {return this.name;};

function TwoDShape(){};
extend(TwoDShape, Shape);
TwoDShape.prototype.name = '2D shape';

function Triangle(side, height) {
  this.side = side;
  this.height = height;
}
extend(Triangle, TwoDShape);
Triangle.prototype.name = 'Triangle';
Triangle.prototype.getArea = function(){return this.side * this.height /
2;};

>>> var myTriangle = new Triangle(5, 10);
>>> myTriangle.getArea() === 25
true
>>> myTriangle.toString() === "Triangle"
true

>>> Triangle.prototype.hasOwnProperty("getArea")
true
>>> Shape.prototype.hasOwnProperty("getArea")
false
>>> Shape.prototype.hasOwnProperty("toString")
true
>>> Triangle.prototype.hasOwnProperty("toString")
false

##Robando el constructor

Otro patrón para implementar la herencia es llamando al constructor padre desde el constructor hijo mediante apply o call.

De esta manera las propiedades del padre son recreadas como propias en el hijo (son valores nuevos, no referencias a objetos)

function Shape(id) {
  this.id = id;
}
Shape.prototype.name = 'shape';
Shape.prototype.toString = function(){return this.name;};

function Triangle() {
  Shape.apply(this, arguments);
}
Triangle.prototype = new Shape();
Triangle.prototype.name = 'Triangle';

>>> var myTriangle = new Triangle(101)
>>> myTriangle.name === "Triangle"
true
>>> myTriangle.toString() === "Triangle"
true
>>> myTriangle.hasOwnProperty("name")
false
>>> myTriangle.hasOwnProperty("id")
true
>>> myTriangle.hasOwnProperty("toString")
false