Herencia de JavaScript [cerrado]

80

Estoy tratando de implementar la herencia en javascript. Se me ocurrió el siguiente código mínimo para admitirlo.

function Base(){
    this.call = function(handler, args){
      handler.call(this, args);
    }
}

Base.extend = function(child, parent){
    parent.apply(child);
    child.base = new parent;
    child.base.child = child;
}

Expertos, háganme saber si esto será suficiente o cualquier otro problema importante que pueda haber pasado por alto. Basado en problemas similares enfrentados, sugiera otros cambios.

Aquí está el script de prueba completo:

function Base(){
    this.call = function(handler, args){
      handler.call(this, args);
    }
    this.superalert = function(){
        alert('tst');
    }
}

Base.extend = function(child, parent){
    parent.apply(child);
    child.base = new parent;
    child.base.child = child;
}

function Child(){
    Base.extend(this, Base);
    this.width = 20;
    this.height = 15;
    this.a = ['s',''];
    this.alert = function(){
        alert(this.a.length);
        alert(this.height);
    }
}

function Child1(){
    Base.extend(this, Child);
    this.depth = 'depth';
    this.height = 'h';
    this.alert = function(){
        alert(this.height); // display current object height
        alert(this.a.length); // display parents array length
        this.call(this.base.alert); 
          // explicit call to parent alert with current objects value
        this.call(this.base.superalert); 
          // explicit call to grandparent, parent does not have method 
        this.base.alert(); // call parent without overriding values
    }
}

var v = new Child1();
v.alert();
alert(v.height);
alert(v.depth);
hambriento
fuente
Si necesita herencia, hay muchas, muchas bibliotecas que ya ofrecen esto. Por lo menos, léalos para averiguar dónde está mal el código. Pero, ¿por qué reinventar? Dos fantásticas bibliotecas de herencia de JavaScript que me vienen a la mente son klass y selfish.js (he usado ambas, son increíbles)
bejonbee
He usado Klass pero hay algún problema al anular las variables de matriz. Intentaré egoísta. Pero mi versión es un código simple de 4 líneas, pero me funciona en la mayoría de los escenarios. Solo quiero saber si me quedaré atrapado más tarde con este enfoque.
hungryMind
2
Es posible que desee revisar esta respuesta SO a una pregunta similar ; Entre todos los grandes consejos, el autor muestra cómo eliminar la llamada al constructor principal al definir la clase secundaria.
bejonbee
@hungryMind: Si está preocupado por problemas particulares relacionados con su código, ¿por qué no edita su pregunta y nos dice exactamente a qué tiene miedo? Porque como solo estás preguntando si tu código está bien, no le da mucha justicia. Probablemente no obtendrá las respuestas que busca. Por lo tanto, le sugiero que edite su Q.
Robert Koritnik
Esta pregunta trata sobre el mismo tema: stackoverflow.com/questions/711209/…
Anderson Green

Respuestas:

139

Para implementar la herencia de JavaScript en ECMAScript 5 , puede definir el prototipo de un objeto y usarlo Object.createpara heredar. También puede agregar / anular propiedades tanto como desee.

Ejemplo:

/**
 * Transform base class
 */
function Transform() {
    this.type = "2d";
}

Transform.prototype.toString = function() {
    return "Transform";
}

/**
 * Translation class.
 */
function Translation(x, y) {
    // Parent constructor
    Transform.call(this);

    // Public properties
    this.x = x;
    this.y = y;
}

// Inheritance
Translation.prototype = Object.create(Transform.prototype);

// Override
Translation.prototype.toString = function() {
    return Transform.prototype.toString() + this.type + " Translation " + this.x + ":" + this.y;
}

/**
 * Rotation class.
 */
function Rotation(angle) {
    // Parent constructor
    Transform.call(this);

    // Public properties
    this.angle = angle;
}

// Inheritance
Rotation.prototype = Object.create(Transform.prototype);

// Override
Rotation.prototype.toString = function() {
    return Transform.prototype.toString() + this.type + " Rotation " + this.angle;
}

// Tests
translation = new Translation(10, 15);

console.log(translation instanceof Transform); // true
console.log(translation instanceof Translation); // true
console.log(translation instanceof Rotation); // false

console.log(translation.toString()) // Transform2d Translation 10:15
Lorenzo Polidori
fuente
2
Translation.prototype = Object.create (new Transform ()); ? Translation.prototype = Object.create (Transform.prototype);
4esn0k
@ 4esn0k eso es correcto, gracias.
Lorenzo Polidori
¿Por qué no solo Translation.prototype = new Transform()? Además, dado que la respuesta no funciona actualmente, ¿la editaría?
Jörn Zaefferer
@ JörnZaefferer Eche un vistazo aquí: stackoverflow.com/q/4166616/885464 . ¿Y qué quieres decir con "la respuesta no funciona actualmente"?
Lorenzo Polidori
4
También debe establecer el constructor de la subclase de forma explícita: Translation.prototype.constructor = Translation. Útil para clonar el objeto (en la mayoría de las técnicas).
barboaz
41

Creo que la solución de Crockfords es demasiado complicada, al igual que la de John. Es mucho más sencillo obtener la herencia de JavaScript de lo que ambos parecen describir. Considerar:

//Classes
function A() {
    B.call(this);
}

function B() {
    C.call(this);
    this.bbb = function() {
        console.log("i was inherited from b!");
    }
}

function C() {
    D.call(this);
}

function D() {
    E.call(this);
}

function E() {
    //instance property 
    this.id = Math.random()
}

//set up the inheritance chain (order matters) 
D.prototype = new E();
C.prototype = new D();
B.prototype = new C();
A.prototype = new B();

//Add custom functions to each
A.prototype.foo = function() {
    console.log("a");
};
B.prototype.bar = function() {
    console.log("b");
};
C.prototype.baz = function() {
    console.log("c");
};
D.prototype.wee = function() {
    console.log("d");
};
E.prototype.woo = function() {
    console.log("e");
};

//Some tests
a = new A();
a.foo();
a.bar();
a.baz();
a.wee();
a.woo();
console.log(a.id);
a.bbb();
console.log(a instanceof A);
console.log(a instanceof B);
console.log(a instanceof C);
console.log(a instanceof D);
console.log(a instanceof E);​
var b = new B();
console.log(b.id)

He escrito una descripción completa de la solución anterior en mi blog .

Marcosc
fuente
1
Excepto que solo admite a todos los miembros públicos
rodrigo-silveira
@ rodrigo-silveira, no estoy seguro de lo que quieres decir. Si quieres privados, simplemente los declaras con var x = "lo que sea", ¿no?
Marcosc
2
Creo que @ rodrigo-silveira quiso decir que no admite miembros protegidos, bueno, ninguna de las soluciones lo hace. (Los miembros privados, por definición, no son accesibles desde la subclase, por lo que eso no tendría sentido). Tienes que usar algo como this._myProtectedVariable = 5;para crear miembros protegidos.
Ciantic
10
muy buena solución, solo (leve) inconveniente, los constructores se ejecutan dos veces. Una vez D.call (this), y nuevamente: new D (). Por lo general, esto no es un gran problema, pero si desea evitarlo, puede usar Object.create así: en lugar de C.prototype = new D (); puede escribir C.prototype = Object.create (D.prototype); ejemplo: jsfiddle.net/9Dxkb/1
Ray Hulha
1
¡Finalmente, una explicación NO CONFUSA que funciona! Invertí tu lógica para hacer que E heredara en la dirección inversa (E tiene más), ya que eso tenía sentido para mí. ¡Gracias!
Ed Bayiates
12

Mientras jugaba con objetos JS, encontré una solución más minimalista :-) ¡Disfruta!

function extend(b,a,t,p) { b.prototype = a; a.apply(t,p); }

Ejemplo

function A() {
    this.info1 = function() {
        alert("A");
    }
}

function B(p1,p2) {
    extend(B,A,this);
    this.info2 = function() {
        alert("B"+p1+p2);
    }
}

function C(p) {
    extend(C,B,this,["1","2"]);
    this.info3 = function() {
        alert("C"+p);
    }
}


var c = new C("c");
c.info1(); // A
c.info2(); // B12
c.info3(); // Cc
Jan Turoň
fuente
8

Aquí está la forma más simple y espero que sea la forma más fácil de entender la herencia en JS. Lo más útil de este ejemplo será para los programadores de PHP.

function Mother(){
    this.canSwim = function(){
        console.log('yes');
    }
}

function Son(){};
Son.prototype = new Mother;
Son.prototype.canRun = function(){
    console.log('yes');
}

Ahora el hijo tiene un método anulado y un nuevo

function Grandson(){}
Grandson.prototype = new Son;
Grandson.prototype.canPlayPiano = function(){
    console.log('yes');
};
Grandson.prototype.canSwim = function(){
    console.log('no');
}

Ahora el nieto tiene dos métodos anulados y uno nuevo.

var g = new Grandson;
g.canRun(); // => yes
g.canPlayPiano(); // => yes
g.canSwim(); // => no
Alexander Serkin
fuente
Seguro que es posible implementarlo como Object.create (new Son)
Alexander Serkin
Eso sería aún peor.
Bergi
4

¿Por qué no usar objetos en lugar de funciones?

var Base = {
    superalert : function() {
        alert('tst');
    }
};

var Child = Object.create(Base);
Child.width = 20;
Child.height = 15;
Child.a = ['s',''];
Child.childAlert = function () {
        alert(this.a.length);
        alert(this.height);
    }

var Child1 = Object.create(Child);
Child1.depth = 'depth';
Child1.height = 'h';
Child1.alert = function () {
    alert(this.height);
    alert(this.a.length);
    this.childAlert();
    this.superalert();
};

Y llámalo así:

var child1 = Object.create(Child1);
child1.alert();

Este enfoque es mucho más limpio que con las funciones. Encontré este blog que explica por qué la herencia con funciones no es una forma adecuada de hacerlo en JS: http://davidwalsh.name/javascript-objects-deconstruction

EDITAR

var Child también se puede escribir como:

var Child = Object.create(Base, {
    width : {value : 20},
    height  : {value : 15, writable: true},
    a : {value : ['s', ''], writable: true},
    childAlert : {value : function () {
        alert(this.a.length);
        alert(this.height);
    }}
});
Luka Blažecki
fuente
4

Aquí está mi solución, que se basa en el método de herencia prototípico estándar descrito en la respuesta de Lorenzo Polidori .

Primero, comienzo definiendo estos métodos auxiliares, que hacen que las cosas sean más fáciles de entender y más legibles más adelante:

Function.prototype.setSuperclass = function(target) {
    // Set a custom field for keeping track of the object's 'superclass'.
    this._superclass = target;

    // Set the internal [[Prototype]] of instances of this object to a new object
    // which inherits from the superclass's prototype.
    this.prototype = Object.create(this._superclass.prototype);

    // Correct the constructor attribute of this class's prototype
    this.prototype.constructor = this;
};

Function.prototype.getSuperclass = function(target) {
    // Easy way of finding out what a class inherits from
    return this._superclass;
};

Function.prototype.callSuper = function(target, methodName, args) {
    // If methodName is ommitted, call the constructor.
    if (arguments.length < 3) {
        return this.callSuperConstructor(arguments[0], arguments[1]);
    }

    // `args` is an empty array by default.
    if (args === undefined || args === null) args = [];

    var superclass = this.getSuperclass();
    if (superclass === undefined) throw new TypeError("A superclass for " + this + " could not be found.");

    var method = superclass.prototype[methodName];
    if (typeof method != "function") throw new TypeError("TypeError: Object " + superclass.prototype + " has no method '" + methodName + "'");

    return method.apply(target, args);
};

Function.prototype.callSuperConstructor = function(target, args) {
    if (args === undefined || args === null) args = [];

    var superclass = this.getSuperclass();
    if (superclass === undefined) throw new TypeError("A superclass for " + this + " could not be found.");

    return superclass.apply(target, args);
};

Ahora, no solo puede establecer la superclase de una clase con SubClass.setSuperclass(ParentClass), sino que también puede llamar a métodos anulados con SubClass.callSuper(this, 'functionName', [argument1, argument2...]):

/**
 * Transform base class
 */
function Transform() {
    this.type = "2d";
}

Transform.prototype.toString = function() {
    return "Transform";
}

/**
 * Translation class.
 */
function Translation(x, y) {
    // Parent constructor
    Translation.callSuper(this, arguments);

    // Public properties
    this.x = x;
    this.y = y;
}
// Inheritance
Translation.setSuperclass(Transform);

// Override
Translation.prototype.toString = function() {
    return Translation.callSuper(this, 'toString', arguments) + this.type + " Translation " + this.x + ":" + this.y;
}

/**
 * Rotation class.
 */
function Rotation(angle) {
    // Parent constructor
    Rotation.callSuper(this, arguments);

    // Public properties
    this.angle = angle;
}
// Inheritance
Rotation.setSuperclass(Transform);

// Override
Rotation.prototype.toString = function() {
    return Rotation.callSuper(this, 'toString', arguments) + this.type + " Rotation " + this.angle;
}

// Tests
translation = new Translation(10, 15);

console.log(translation instanceof Transform); // true
console.log(translation instanceof Translation); // true
console.log(translation instanceof Rotation); // false

console.log(translation.toString()) // Transform2d Translation 10:15

Es cierto que incluso con las funciones de ayuda, la sintaxis aquí es bastante incómoda. Afortunadamente, sin embargo, en ECMAScript 6 se ha agregado algo de azúcar sintáctico ( clases mínimas máximas ) para hacer las cosas mucho más bonitas. P.ej:

/**
 * Transform base class
 */
class Transform {
  constructor() {
    this.type = "2d";
  }

  toString() {
    return "Transform";
  } 
}

/**
 * Translation class.
 */
class Translation extends Transform {
  constructor(x, y) {
    super(); // Parent constructor

    // Public properties
    this.x = x;
    this.y = y;
  }

  toString() {
    return super(...arguments) + this.type + " Translation " + this.x + ":" + this.y;
  }
}

/**
 * Rotation class.
 */
class Rotation extends Transform {
  constructor(angle) {
    // Parent constructor
    super(...arguments);

    // Public properties
    this.angle = angle;
  }

  toString() {
    return super(...arguments) + this.type + " Rotation " + this.angle;
  }
}

// Tests
translation = new Translation(10, 15);

console.log(translation instanceof Transform); // true
console.log(translation instanceof Translation); // true
console.log(translation instanceof Rotation); // false

console.log(translation.toString()) // Transform2d Translation 10:15

Tenga en cuenta que ECMAScript 6 todavía se encuentra en la etapa de borrador en este momento, y que yo sepa no está implementado en ningún navegador web importante. Sin embargo, si lo desea, puede usar algo como el compilador de Traceur para compilar ECMAScript 6en el ECMAScript 5JavaScript simple y antiguo . Puede ver el ejemplo anterior compilado con Traceur aquí .

Ajedi32
fuente
2

Si bien estoy de acuerdo con todas las respuestas anteriores, creo que JavaScript no necesita estar orientado a objetos (evitar la herencia), sino que un enfoque basado en objetos debería ser suficiente en la mayoría de los casos.

Me gusta la forma en que Eloquent JavaScript comienza su Capítulo 8 sobre Programación Orientada a Objetos hablando de OO. En lugar de descifrar la mejor manera de implementar la herencia, se debería dedicar más energía a aprender los aspectos funcionales de JavaScript, por lo tanto, encontré el Capítulo 6 sobre Programación Funcional, más interesante.

Anand
fuente
2
//This is an example of how to override a method, while preserving access to the original.
//The pattern used is actually quite simple using JavaScripts ability to define closures:

    this.somefunction = this.someFunction.override(function(args){
        var result = this.inherited(args);
        result += this.doSomethingElse();
        return result;
    });

//It is accomplished through this piece of code (courtesy of Poul Krogh):

/***************************************************************
    function.override overrides a defined method with a new one, 
    while preserving the old method.
    The old method is only accessible from the new one.
    Use this.inherited() to access the old method.
***************************************************************/

    Function.prototype.override = function(func)
    {
        var remember = this;
        var f = function() 
        {
             var save = this.inherited; 
             this.inherited = remember;
             var result = func.apply(this, Array.prototype.slice.call(arguments));
             this.inherited = save;
             return result;
        };
        return f;
    }
Lars Christensen
fuente
1

¿Qué tal este enfoque simple

    function Body(){
        this.Eyes = 2;
        this.Arms = 2;
        this.Legs = 2;
        this.Heart = 1;
        this.Walk = function(){alert(this.FirstName + ' Is Walking')};
    }

    function BasePerson() {
        var BaseBody = new Body(this);
        BaseBody.FirstName = '';
        BaseBody.LastName = '';
        BaseBody.Email = '';
        BaseBody.IntroduceSelf = function () { alert('Hello my name is ' + this.FirstName + ' ' + this.LastName); };
        return BaseBody;
    }

    function Person(FirstName,LastName)
    {
        var PersonBuild = new BasePerson();
        PersonBuild.FirstName = FirstName;
        PersonBuild.LastName = LastName;
        return PersonBuild;
    }

    var Person1 = new Person('Code', 'Master');
    Person1.IntroduceSelf();
    Person1.Walk();
CodeMilian
fuente
1

Herencia prototípica básica

Una forma simple pero efectiva de hacer herencia en JavaScript es usar las siguientes dos líneas:

B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;

Eso es similar a hacer esto:

B.prototype = new A();

La principal diferencia entre ambos es que el constructor de Ano se ejecuta cuando se usa Object.create, lo cual es más intuitivo y más similar a la herencia basada en clases.

Siempre puede optar por ejecutar opcionalmente el constructor de Aal crear una nueva instancia de Bagregando agregarlo al constructor de B:

function B(arg1, arg2) {
    A(arg1, arg2); // This is optional
}

Si desea pasar todos los argumentos de Ba A, también puede usar Function.prototype.apply():

function B() {
    A.apply(this, arguments); // This is optional
}

Si desea mezclar otro objeto en la cadena de constructores de B, puede combinar Object.createcon Object.assign:

B.prototype = Object.assign(Object.create(A.prototype), mixin.prototype);
B.prototype.constructor = B;

Manifestación

function A(name) {
  this.name = name;
}

A.prototype = Object.create(Object.prototype);
A.prototype.constructor = A;

function B() {
  A.apply(this, arguments);
  this.street = "Downing Street 10";
}

B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;

function mixin() {

}

mixin.prototype = Object.create(Object.prototype);
mixin.prototype.constructor = mixin;

mixin.prototype.getProperties = function() {
  return {
    name: this.name,
    address: this.street,
    year: this.year
  };
};

function C() {
  B.apply(this, arguments);
  this.year = "2018"
}

C.prototype = Object.assign(Object.create(B.prototype), mixin.prototype);
C.prototype.constructor = C;

var instance = new C("Frank");
console.log(instance);
console.log(instance.getProperties());


Creando tu propia envoltura

Si no le gusta escribir aproximadamente las mismas dos líneas en todo su código, puede escribir una función de envoltura básica como esta:

function inheritance() {
  var args = Array.prototype.slice.call(arguments);
  var firstArg = args.shift();
  switch (args.length) {
  case 0:
    firstArg.prototype = Object.create(Object.prototype);
    firstArg.prototype.constructor = firstArg;
    break;
  case 1:
    firstArg.prototype = Object.create(args[0].prototype);
    firstArg.prototype.constructor = firstArg;
    break;
  default:
    for(var i = 0; i < args.length; i++) {
      args[i] = args[i].prototype;
    }
    args[0] = Object.create(args[0]);
    var secondArg = args.shift();
    firstArg.prototype = Object.assign.apply(Object, args);
    firstArg.prototype.constructor = firstArg;
  }
}

Cómo funciona esta envoltura:

  1. Si pasa un parámetro, su prototipo heredará Object.
  2. Si pasa dos parámetros, el prototipo del primero heredará del segundo.
  3. Si pasa más de dos parámetros, el prototipo del primero heredará del segundo y se mezclarán los prototipos de otros parámetros.

Manifestación

function inheritance() {
  var args = Array.prototype.slice.call(arguments);
  var firstArg = args.shift();
  switch (args.length) {
  case 0:
    firstArg.prototype = Object.create(Object.prototype);
    firstArg.prototype.constructor = firstArg;
    break;
  case 1:
    firstArg.prototype = Object.create(args[0].prototype);
    firstArg.prototype.constructor = firstArg;
    break;
  default:
    for(var i = 0; i < args.length; i++) {
      args[i] = args[i].prototype;
    }
    args[0] = Object.create(args[0]);
    var secondArg = args.shift();
    firstArg.prototype = Object.assign.apply(Object, args);
    firstArg.prototype.constructor = firstArg;
  }
}

function A(name) {
  this.name = name;
}

inheritance(A);

function B() {
  A.apply(this, arguments);
  this.street = "Downing Street 10";
}

inheritance(B, A);

function mixin() {

}

inheritance(mixin);

mixin.prototype.getProperties = function() {
  return {
    name: this.name,
    address: this.street,
    year: this.year
  };
};

function C() {
  B.apply(this, arguments);
  this.year = "2018"
}

inheritance(C, B, mixin);

var instance = new C("Frank");
console.log(instance);
console.log(instance.getProperties());


Nota

Object.createse puede utilizar de forma segura en todos los navegadores modernos, incluido IE9 +. Object.assignno funciona en ninguna versión de IE ni en algunos navegadores móviles. Se recomienda polyfill Object.create y / oObject.assign si desea utilizarlos y admitir navegadores que no los implementan.

Puede encontrar un polyfill para Object.create aquí y uno para Object.assign aquí .

John Slegers
fuente
0
//
//  try this one:
//  
//    function ParentConstructor() {}
//    function ChildConstructor()  {}
//
//    var 
//        SubClass = ChildConstructor.xtendz( ParentConstructor );
//
Function.prototype.xtendz = function ( SuperCtorFn ) {

    return ( function( Super, _slice ) {

                // 'freeze' host fn 
                var
                    baseFn = this, 
                    SubClassCtorFn;

                // define child ctor
                SubClassCtorFn = function ( /* child_ctor_parameters..., parent_ctor_parameters[] */ ) {

                    // execute parent ctor fn on host object
                    // pass it last ( array ) argument as parameters
                    Super.apply( this, _slice.call( arguments, -1 )[0] );

                    // execute child ctor fn on host object
                    // pass remaining arguments as parameters
                    baseFn.apply( this, _slice.call( arguments, 0, -1 ) );

                };

                // establish proper prototype inheritance
                // 'inherit' methods
                SubClassCtorFn.prototype = new Super;

                // (re)establish child ctor ( instead of Super ctor )
                SubClassCtorFn.prototype.constructor = SubClassCtorFn;

                // return built ctor
                return SubClassCtorFn;

    } ).call( this, SuperCtorFn, Array.prototype.slice );
};

// declare parent ctor
function Sup( x1, x2 ) {
    this.parent_property_1 = x1;
    this.parent_property_2 = x2;
}

// define some methods on parent
Sup.prototype.hello = function(){ 
   alert(' ~  h e l l o   t h e r e  ~ ');
};


// declare child ctor
function Sub( x1, x2 ) {
    this.child_property_1 = x1;
    this.child_property_2 = x2;
}

var
    SubClass = Sub.xtendz(Sup), // get 'child class' ctor
    obj;

// reserve last array argument for parent ctor
obj = new SubClass( 97, 98, [99, 100] ); 

obj.hello();

console.log( obj );
console.log('obj instanceof SubClass      -> ', obj instanceof SubClass      );
console.log('obj.constructor === SubClass -> ', obj.constructor === SubClass );
console.log('obj instanceof Sup           -> ', obj instanceof Sup           );
console.log('obj instanceof Object        -> ', obj instanceof Object        );

//
//  Object {parent_property_1: 99, parent_property_2: 100, child_property_1: 97, child_property_2: 98}
//  obj instanceof SubClass      -> true
//  obj.constructor === SubClass -> true
//  obj instanceof Sup           -> true
//  obj instanceof Object        -> true
//
anulación pública
fuente
-1

La forma más sencilla de utilizar la biblioteca AWeb . Muestra oficial:

   /**
     * A-class
     */
   var ClassA = AWeb.class({
     public : {
        /**
          * A-class constructor
          */
        constructor : function() {
           /* Private variable */
           this.variable1 = "A";
           this.calls = 0;
        },

        /**
          * Function returns information about the object
          */
        getInfo : function() {
           this.incCalls();

           return "name=" + this.variable1 + ", calls=" + this.calls;
        }
     },
     private : {
        /**
          * Private function
          */
        incCalls : function() {
           this.calls++;
        }
     }
  });
  /**
    * B-class
    */
  var ClassB = AWeb.class({
     extends : ClassA,
     public : {
        /**
          * B-class constructor
          */
        constructor : function() {
           this.super();

           /* Private variable */
           this.variable1 = "B";
        },

        /**
          * Function returns extended information about the object
          */
        getLongInfo : function() {
           return this.incCalls !== undefined ? "incCalls exists" : "incCalls undefined";
        }
     }
  });
  /**
    * Main project function
    */
  function main() {
     var a = new ClassA(),
         b = new ClassB();

     alert(
        "a.getInfo " + (a.getInfo ? "exists" : "undefined") + "\n" +
        "a.getLongInfo " + (a.getLongInfo ? "exists" : "undefined") + "\n" +

        "b.getInfo " + (b.getInfo ? "exists" : "undefined") + "\n" +
        "b.getLongInfo " + (b.getLongInfo ? "exists" : "undefined") + "\n" +

        "b.getInfo()=" + b.getInfo() + "\n" +
        "b.getLongInfo()=" + b.getLongInfo()
     );
  }
WebUser
fuente
-1

Encontré una solución mucho más fácil que extender y hacer prototipos. En realidad, no sé qué tan eficiente es esto, aunque parece limpio y funcional.

var A = function (p) {
    if (p == null) p = this;
    p.a1 = 0;
    this.a2 = 0;
    var a3 = 0;
};

var B = function (p) {
    if (p == null) p = this;
    p.b1 = new A(this);
    this.b2 = new A(this);
    var b3 = new A(this);
    this b4 = new A();
};

var a = new A ();
var b = new B ();

resultado:

a
    a1        0
    a2        0
b
    a1        0
    b1
        a2    0
    b2
        a2    0
    b4
        a1    0
        a2    0

ejemplo práctico:

var Point = function (p) {
    if (p == null) p = this;
    var x = 0;
    var y = 0;
    p.getPoint = function () { return [x,y]; };
    p.setPoint = function (_x,_y) { x = _x; y = _y; };
};

var Dimension = function (p) {
    if (p == null) p = this;
    var w = 0;
    var h = 0;
    p.getDimension = function() { return [w,h] };
    p.setDimension = function(_w,_h) { w = _w; h = _h };
};

var Rect = function (p) {
    if (p == null) p = this;
    var dimension = new Dimension(this);
    var location  = new Point(this);
};

var rect = new Rect ();
rect.setDimension({w:30,h:40});
rect.setPoint({x:50,y:50});
cortina
fuente