Спадкування JavaScript [закрито]


80

Я намагаюся реалізувати успадкування у javascript. Я придумав наступний мінімальний код для його підтримки.

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

Експерти, дайте мені знати, чи цього буде достатньо чи якесь інше важливе питання, яке я міг пропустити. На основі подібних проблем, будь-ласка, запропонуйте інші зміни.

Ось повний сценарій тесту:

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

Якщо вам потрібна спадщина, існує безліч бібліотек, які вже пропонують це. Принаймні прочитайте їх, щоб з’ясувати, де ви неправильно кодуєте. Але навіщо заново винаходити? Дві чудові бібліотеки успадкування javascript, які приходять мені на думку, це klass та selfish.js (я використовував обидві, вони дивовижні.)
bejonbee,

Я використовував Klass, але є певна проблема в перевизначенні змінних масиву. Я спробую егоїстично. Але моя версія - це простий 4-рядковий код, але він працює для мене в більшості випадків. Я просто хочу знати, чи не затримаюся я пізніше при такому підході.
HungryMind

2
Можливо, ви захочете переглянути цю відповідь на подібне запитання ; серед усіх чудових порад автор показує, як видалити виклик батьківського конструктора при визначенні дочірнього класу.
bejonbee

@hungryMind: Якщо вас турбують певні проблеми, пов’язані з вашим кодом, чому б вам не відредагувати своє запитання та не повідомити нам, чого саме ви боїтеся? Тому що, оскільки ви просто запитуєте, чи добре ваш код, це не надає йому великої справедливості. Ви, мабуть, не отримаєте відповіді, які шукаєте. Тому я пропоную вам відредагувати ваше
запитання.

Це питання стосується тієї самої теми: stackoverflow.com/questions/711209/…
Андерсон Грін

Відповіді:


139

Для реалізації успадкування javascript у ECMAScript 5 ви можете визначити прототип об’єкта та використовувати Object.createдля успадкування. Ви також можете додавати / перевизначати властивості скільки завгодно.

Приклад:

/**
 * 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 (new Transform ()); ? Translation.prototype = Object.create (Transform.prototype);
4esn0k

@ 4esn0k це правильно, дякую.
Лоренцо Полідорі

Чому не просто Translation.prototype = new Transform()? Крім того, оскільки відповідь зараз не працює, чи можете ви її редагувати?
Jorn Zaefferer

@ JörnZaefferer Подивіться тут: stackoverflow.com/q/4166616/885464 . І що ви маєте на увазі під назвою "відповідь наразі не працює"?
Лоренцо Полідорі

4
Ви повинні також встановити конструктор підкласу в явному вигляді: Translation.prototype.constructor = Translation. Корисно для клонування об’єкта (у більшості технік).
barboaz

41

Я думаю, що рішення Crockfords занадто складне, як і рішення Джона. Отримати спадщину javascript набагато простіше, ніж вони обидва описують. Розглянемо:

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

Я написав повний опис вищевказаного рішення у своєму блозі .


1
За винятком того, що він підтримує лише всіх публічних членів
rodrigo-silveira

@ rodrigo-silveira, не знаю, що ти маєш на увазі. Якщо ви хочете приватних осіб, ви просто оголошуєте їх за допомогою var x = "все, що завгодно", ні?
Маркоск

2
Я думаю, що @ rodrigo-silveira мав на увазі, що він не підтримує захищені члени, а також жодне рішення не підтримує. (До приватних членів за визначенням неможливо отримати доступ з підкласу, що не має сенсу) Ви повинні використовувати щось на зразок this._myProtectedVariable = 5;створення захищених членів.
Ciantic

10
дуже приємне рішення, лише (незначний) недолік, конструктори виконуються двічі. Одного разу D.call (це), і знову: new D (). Зазвичай це не становить великих проблем, але якщо ви хочете цього уникнути, ви можете використовувати Object.create таким чином: замість C.prototype = new D (); ви можете написати C.prototype = Object.create (D.prototype); приклад: jsfiddle.net/9Dxkb/1
Ray Hulha

1
Нарешті, НЕПУТНЕ пояснення, яке працює! Я змінив вашу логіку, щоб зробити так, щоб Е успадковував у зворотному напрямку (у Е найбільше), оскільки це мало сенс для мене. Дякую!
Ед Байятес,

12

Погравши з об’єктами JS, я знайшов більш мінімалістичне рішення :-) Насолоджуйтесь!

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

Приклад

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

Ось найпростіший і, сподіваюся, найпростіший спосіб зрозуміти спадщину в JS. Найбільш корисним цей приклад буде для програмістів PHP.

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

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

Зараз у сина є один перевизначений метод і один новий

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

Зараз онук має два перевизначені методи і один новий

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


Звичайно, це можливо реалізувати як Object.create (new Son)
Олександр Серкін

Це було б ще гірше.
Бергі

4

Чому б не використовувати об'єкти замість функцій:

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

І назвіть це так:

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

Цей підхід набагато чіткіший, ніж функціональний. Я знайшов цей блог, що пояснює, чому успадкування з функціями не є правильним способом зробити це в JS: http://davidwalsh.name/javascript-objects-deconstruction

РЕДАГУВАТИ

var Child також можна записати як:

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

Ось моє рішення, яке засноване на стандартному прототип методи спадкування , описаної в Лоренца Полідорьте відповідь .

По-перше, я починаю з визначення цих допоміжних методів, які полегшують розуміння та читання пізніше:

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

Тепер ви можете не тільки встановити суперклас класу за допомогою SubClass.setSuperclass(ParentClass), але і викликати перевизначені методи за допомогою 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

Слід визнати, що навіть з допоміжними функціями синтаксис тут досить незручний. На щастя, в ECMAScript 6 було додано деякий синтаксичний цукор ( максимально мінімальні класи ), щоб зробити речі набагато гарнішими. Наприклад:

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

Зверніть увагу, що ECMAScript 6 на даний момент все ще знаходиться на стадії проекту, і, наскільки мені відомо, він не реалізований в жодному великому веб-браузері. Однак, якщо ви хочете, ви можете використовувати щось на зразок компілятора Traceur для компіляції ECMAScript 6до простого старого ECMAScript 5JavaScript. Ви можете побачити наведений вище приклад, складений за допомогою Traceur тут .


2

Хоча я погоджуюся з усіма наведеними вище відповідями, я вважаю, що JavaScript не повинен бути об’єктно-орієнтованим (уникати успадкування), натомість об’єктний підхід у більшості випадків повинен бути достатнім.

Мені подобається те, як Eloquent JavaScript починає свою главу 8 з об’єктно-орієнтованого програмування, в якій йдеться про ОО. Замість того, щоб розшифровувати найкращий спосіб реалізації спадщини, слід приділити більше енергії для вивчення функціональних аспектів JavaScript, отже, мені здалося, що глава 6 про функціональне програмування є більш цікавою.


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

Як щодо цього простого підходу

    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

Основне прототипне успадкування

Простим, але ефективним способом успадкування в JavaScript є використання наступного дворядкового рядка:

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

Це схоже на це:

B.prototype = new A();

Основна відмінність між ними полягає в тому, що конструктор Aне запускається під час використання Object.create, що є більш інтуїтивно зрозумілим та більш схожим на успадкування на основі класу.

Ви завжди можете вибрати додатковий запуск конструктора Aпри створенні нового екземпляра B, додавши його до конструктора B:

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

Якщо ви хочете передати всі аргументи Bдо A, ви також можете використовувати Function.prototype.apply():

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

Якщо ви хочете змішати інший об'єкт у ланцюжку конструкторів B, ви можете комбінувати Object.createз Object.assign:

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

Демо

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


Створення власної обгортки

Якщо вам не подобається писати приблизно один і той же дворядковий рядок у коді, ви можете написати базову функцію обгортки, як це:

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

Як працює ця обгортка:

  1. Якщо ви передасте один параметр, його прототип успадкує від Object.
  2. Якщо ви передасте два параметри, прототип першого успадкує від другого.
  3. Якщо ви передасте більше двох параметрів, перший прототип успадкує від другого, а прототипи інших параметрів будуть змішані.

Демо

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


Примітка

Object.createможна безпечно використовувати в будь-якому сучасному браузері, включаючи IE9 +. Object.assignне працює ні в одній версії IE, ні в деяких мобільних браузерах. Рекомендується заповнювати Object.create і / або Object.assignякщо ви хочете використовувати їх, а також підтримувати браузери, які їх не реалізують.

Ви можете знайти поліфіл Object.create тут і один Object.assign тут .


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

Найпростіший спосіб використання бібліотеки AWeb . Офіційний зразок:

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

Я знайшов рішення набагато простіше, ніж розширити та прототипувати речі. Насправді я не знаю, наскільки це ефективно, хоча виглядає чисто і функціонально.

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

результат:

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

практичний приклад:

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});
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.