Dziedziczenie JavaScript [zamknięte]


80

Próbuję zaimplementować dziedziczenie w javascript. Wymyśliłem następujący minimalny kod do obsługi tego.

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;
}

Eksperci, dajcie mi znać, czy to wystarczy lub jakaś inna ważna kwestia, którą mogłam przegapić. W oparciu o podobne problemy prosimy o zaproponowanie innych zmian.

Oto kompletny skrypt testowy:

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);

Jeśli potrzebujesz dziedziczenia, istnieje wiele, wiele bibliotek, które już to oferują. Przeczytaj je przynajmniej, aby dowiedzieć się, gdzie kod jest zły. Ale po co wymyślać na nowo? Dwie niesamowite biblioteki dziedziczenia javascript, które przychodzą na myśl, to klass i selfish.js (użyłem obu, są niesamowite).
bejonbee

Użyłem Klass, ale występuje problem z zastępowaniem zmiennych tablicowych. Spróbuję samolubnie. Ale moja wersja jest prostym 4-liniowym kodem, ale działa dla mnie w większości scenariusza. Chcę tylko wiedzieć, czy później utknę przy takim podejściu.
hungryMind

2
Możesz przejrzeć tę odpowiedź SO na podobne pytanie ; spośród wszystkich świetnych wskazówek autor pokazuje, jak usunąć wywołanie konstruktora rodzica podczas definiowania klasy potomnej.
bejonbee

@hungryMind: Jeśli martwisz się konkretnymi problemami związanymi z Twoim kodem, zmień swoje pytanie i nie powiedz nam dokładnie, czego się boisz. Ponieważ pytasz tylko, czy twój kod jest w porządku, nie oddajesz mu zbytniej sprawiedliwości. Prawdopodobnie nie otrzymasz odpowiedzi, których szukasz. Dlatego sugeruję edycję Q.
Robert Koritnik

To pytanie dotyczy tego samego tematu: stackoverflow.com/questions/711209/…
Anderson Green,

Odpowiedzi:


139

Aby zaimplementować dziedziczenie javascript w ECMAScript 5 , możesz zdefiniować prototyp obiektu i użyć go Object.createdo dziedziczenia. Możesz także dodawać / zastępować właściwości, ile chcesz.

Przykład:

/**
 * 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

2
Translation.prototype = Object.create (nowa Transform ()); ? Translation.prototype = Object.create (Transform.prototype);
4esn0k

@ 4esn0k zgadza się, dzięki.
Lorenzo Polidori

Dlaczego nie po prostu Translation.prototype = new Transform()? A skoro odpowiedź obecnie nie działa, czy zmodyfikowałbyś ją?
Jörn Zaefferer

@ JörnZaefferer Zajrzyj tutaj: stackoverflow.com/q/4166616/885464 . A co masz na myśli, mówiąc „odpowiedź obecnie nie działa”?
Lorenzo Polidori

4
Należy również ustawić konstruktora podklasy wyraźnie: Translation.prototype.constructor = Translation. Przydatne do klonowania obiektu (w większości technik).
barboaz

41

Myślę, że rozwiązanie Crockforda jest zbyt skomplikowane, podobnie jak rozwiązanie Johna. O wiele prostsze jest uzyskanie dziedziczenia javascript, niż oba wydają się opisywać. Rozważać:

//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)

Pełny opis powyższego rozwiązania napisałem na swoim blogu .


1
Tyle że obsługuje tylko wszystkich członków publicznych
rodrigo-silveira

@ rodrigo-silveira, nie jestem pewien, co masz na myśli. Jeśli chcesz szeregowców, po prostu zadeklaruj je za pomocą var x = "cokolwiek", nie?
Marcosc

2
Myślę, że @ rodrigo-silveira oznaczało, że nie obsługuje chronionych członków, cóż, żadne rozwiązanie nie działa. (Prywatni członkowie z definicji nie są dostępni z podklasy, więc nie miałoby to sensu). Musisz użyć czegoś takiego, jak this._myProtectedVariable = 5;tworzenie chronionych członków.
Ciantic

10
bardzo fajne rozwiązanie, tylko (niewielki) mankament, konstruktorzy są wykonywane dwukrotnie. Raz D. zadzwoń (this) i jeszcze raz: new D (). Zwykle nie jest to duży problem, ale jeśli chcesz tego uniknąć, możesz użyć Object.create w następujący sposób: zamiast C.prototype = new D (); możesz napisać C.prototype = Object.create (D.prototype); przykład: jsfiddle.net/9Dxkb/1
Ray Hulha

1
Wreszcie, BEZWŁĄTNE wyjaśnienie, które działa! Odwróciłem twoją logikę, aby E dziedziczyło w odwrotnym kierunku (E ma najwięcej do tego), ponieważ miało to dla mnie sens. Dzięki!
Ed Bayiates,

12

Kiedy bawiłem się obiektami JS, znalazłem bardziej minimalistyczne rozwiązanie :-) Ciesz się!

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

Przykład

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

8

Oto najprostszy i mam nadzieję, że najłatwiejszy sposób zrozumienia dziedziczenia w JS. Najbardziej pomocny przykład będzie dla programistów PHP.

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

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

Teraz syn ma jedną zastąpioną metodę i jedną nową

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

Teraz wnuk ma dwie nadpisane metody i jedną nową

var g = new Grandson;
g.canRun(); // => yes
g.canPlayPiano(); // => yes
g.canSwim(); // => no


Jasne, że można to zaimplementować jako Object.create (new Son)
Alexander Serkin

To byłoby jeszcze gorsze.
Bergi

4

Dlaczego nie używać obiektów zamiast funkcji:

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();
};

I nazwij to tak:

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

To podejście jest znacznie bardziej przejrzyste niż w przypadku funkcji. Znalazłem ten blog wyjaśniający, dlaczego dziedziczenie z funkcjami nie jest właściwym sposobem robienia tego w JS: http://davidwalsh.name/javascript-objects-deconstruction

EDYTOWAĆ

var Dziecko można również zapisać jako:

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);
    }}
});

4

Oto moje rozwiązanie, które opiera się na standardowym prototypowego dziedziczenia metody opisanej w Lorenzo Polidori za odpowiedź .

Najpierw zacznę od zdefiniowania tych pomocniczych metod, które później ułatwiają zrozumienie i czytelność:

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);
};

Teraz nie tylko możesz ustawić nadklasę klasy za pomocą SubClass.setSuperclass(ParentClass), ale możesz także wywołać metody nadpisane za pomocą 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

Trzeba przyznać, że nawet w przypadku funkcji pomocniczych składnia jest tutaj dość niezręczna. Na szczęście w ECMAScript 6 dodano trochę cukru syntaktycznego ( maksymalnie minimalne klasy ), aby uczynić rzeczy znacznie ładniejszymi. Na przykład:

/**
 * 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

Zauważ, że ECMAScript 6 jest nadal w fazie roboczej i o ile wiem, nie jest zaimplementowany w żadnej większej przeglądarce internetowej. Jeśli jednak chcesz, możesz użyć czegoś takiego jak kompilator Traceur, aby skompilować ECMAScript 6się do zwykłego, starego ECMAScript 5JavaScript. Możesz zobaczyć powyższy przykład skompilowany przy użyciu Traceur tutaj .


2

Chociaż zgadzam się ze wszystkimi powyższymi odpowiedziami, uważam, że JavaScript nie musi być zorientowany obiektowo (unikaj dziedziczenia), zamiast tego w większości przypadków wystarczające powinno być podejście oparte na obiektach .

Podoba mi się sposób, w jaki Eloquent JavaScript rozpoczyna rozdział 8 poświęcony programowaniu obiektowemu, mówiąc o obiektach obiektowych. Zamiast rozszyfrować najlepszy sposób implementacji dziedziczenia, należy poświęcić więcej energii na naukę funkcjonalnych aspektów JavaScript, stąd bardziej interesujący wydał mi się rozdział 6 dotyczący programowania funkcjonalnego.


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;
    }

1

Co powiesz na to proste podejście

    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();

1

Podstawowe dziedziczenie prototypowe

Prostym, ale skutecznym sposobem dziedziczenia w JavaScript jest użycie następującego dwuwierszowego:

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

To jest podobne do zrobienia tego:

B.prototype = new A();

Główna różnica między nimi polega na tym, że konstruktor programu Anie jest uruchamiany podczas używania Object.create, co jest bardziej intuicyjne i bardziej podobne do dziedziczenia opartego na klasach.

Zawsze możesz opcjonalnie uruchomić konstruktora programu Apodczas tworzenia nowej instancji programu B, dodając go do konstruktora B:

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

Jeśli chcesz przekazać wszystkie argumenty funkcji Bdo A, możesz również użyć Function.prototype.apply():

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

Jeśli chcesz dodać inny obiekt do łańcucha konstruktorów B, możesz połączyć Object.createz Object.assign:

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

Próbny

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());


Tworzenie własnego opakowania

Jeśli nie lubisz pisać z grubsza tego samego dwuwierszowego kodu w całym kodzie, możesz napisać podstawową funkcję opakowującą, taką jak ta:

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;
  }
}

Jak działa to opakowanie:

  1. Jeśli przekażesz jeden parametr, jego prototyp odziedziczy po Object.
  2. Jeśli przekażesz dwa parametry, prototyp pierwszego odziedziczy po drugim.
  3. Jeśli przekażesz więcej niż dwa parametry, prototyp pierwszego odziedziczy po drugim, a prototypy pozostałych parametrów zostaną wmieszane.

Próbny

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());


Uwaga

Object.createmoże być bezpiecznie używany w każdej nowoczesnej przeglądarce, w tym IE9 +. Object.assignnie działa w żadnej wersji IE ani w niektórych przeglądarkach mobilnych. Zaleca się wypełnianie w formie polyfillObject.create i / lub Object.assignjeśli chcesz ich używać i obsługiwać przeglądarki, które ich nie implementują.

Możesz znaleźć polyfill dla Object.create tutaj i jeden dla Object.assign tutaj .


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
//

-1

Najłatwiejszy sposób korzystania z biblioteki AWeb . Oficjalna próbka:

   /**
     * 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()
     );
  }

-1

Znalazłem rozwiązanie znacznie łatwiejsze niż rozbudowa i prototypowanie rzeczy. Właściwie nie wiem, na ile to jest wydajne, chociaż wygląda czysto i funkcjonalnie.

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 ();

wynik:

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

praktyczny przykład:

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});
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.