Javascript, внутренние классы и как эффективно получить доступ к родительской области

В Javascript я хотел бы определить класс с внутренним (или вложенным) классом. Во внутреннем классе я хотел бы получить доступ к родительскому экземпляру. Как я могу сделать это эффективно?

Некоторый код покажет, что я имею в виду. Предположим, я определяю класс MyType1, который предоставляет несколько свойств и одну функцию SayHello:

(function(){
    MyType1 = function(name){
        this.TypeName = "MyType1";
        this.Name = name;
    };

    MyType1.prototype.SayHello = function() {
        say(this.Name + " says hello...");
    };
})();

Хорошо, теперь, начиная оттуда, я хочу добавить «внутренний класс» в MyType1, поэтому я добавляю новый код, чтобы он выглядел так:

(function(){
    MyType1 = function(name){
        this.TypeName = "MyType1";
        this.Name = name;
        var parentName = name;
        this.Child = function(name) {
            this.Description = parentName + "'s child, " + name;
        };

        this.Child.prototype.Introduce = function() {
            say(this.Description + ", greets you...");
        };
    };

    MyType1.prototype.SayHello = function() {
        say(this.Name + " says hello...");
    };
})();

Теперь я могу использовать эти классы следующим образом:

var x = new MyType1("Victor");
x.SayHello();

var c = new x.Child("Elizabeth");
c.Introduce();

что все работает. Но он определяет новую дочернюю функцию (или тип, если хотите) для каждого экземпляра MyType1. Что я хотел бы сделать, так это получить доступ к области родительского класса, не прибегая к этой неэффективности. Что-то вроде этого:

(function(){
    MyType2 = function(name){
        this.TypeName = "MyType2";
        this.Name = name;
        this.Prop1 = 1872;
        var parentName = name;
    };

    MyType2.prototype.SayHello = function() {
        say(this.Name + " says hello...");
    };

    var c1 = function(name) {
        this.Description = parentName + "'s child, " + name;
        //                ^^ no go! ^^
    };

    c1.prototype.Introduce = function() {
        say(this.Description + ", greets you...");
    };

    MyType2.prototype.Child = c1;

})();

Но это не работает. Конечно, переменная parentName выходит за рамки.

Есть ли эффективный способ для экземпляра Child (в конструкторе или в любой функции класса) получить доступ к родительскому экземпляру (MyType2)?


Я знаю, что могу определить класс Child как независимый, невложенный класс, а затем в ctor для этого просто передать экземпляр Parent. Но это создает N ссылок на родительский экземпляр, по одной на каждый дочерний экземпляр. Это похоже на неэффективность, которую я хотел бы избежать.

спасибо за любые советы.


EDIT - причина, по которой я хочу, чтобы дочерний элемент имел доступ к родительскому экземпляру, заключается в том, что родительский объект содержит объект, создание которого довольно дорого - что-то вроде соединения с базой данных - и я хотел бы, чтобы дочерний элемент чтобы иметь возможность использовать эту вещь.


person Cheeso    schedule 22.01.2011    source источник
comment
Тьфу, еще одно злоупотребление Javascript, чтобы привнести ерунду ООП. В этом языке нет классов: если они вам нужны, используйте то, что есть!   -  person Lightness Races in Orbit    schedule 22.01.2011
comment
Вы можете найти это полезным: blog.niftysnippets.org/2009/ 09/ Это набор вспомогательных функций (и их объяснение), который предоставляет простые, но эффективные средства создания подклассов классов, созданных с помощью прототипического наследования. Похоже, вы делаете это довольно вручную, так что это может избавить вас от некоторых проблем.   -  person T.J. Crowder    schedule 22.01.2011
comment
@Tomalek: Вы совершенно правы, конечно, в JavaScript нет классов. Однако он объектно-ориентирован и имеет наследование.   -  person T.J. Crowder    schedule 22.01.2011
comment
Я не понимаю, почему вам нужно передавать ссылку на родительский экземпляр дочернему, если вы придерживаетесь своей последней идеи. Вам просто нужно передать parentName, если это все, что вы собираетесь использовать в дочернем классе. Мне кажется, что это правильный способ OO для достижения того, что вы пытаетесь сделать.   -  person KaptajnKold    schedule 22.01.2011
comment
@Cheeso Какова цель оболочки анонимной функции?   -  person Šime Vidas    schedule 22.01.2011
comment
Используйте ключевое слово var для определения локальных переменных. Если вы хотите определить глобальные переменные (пожалуйста, не делайте этого), определите их явно как свойства глобальной области видимости.   -  person Pointy    schedule 22.01.2011
comment
@Tomalak Какой язык, кроме JavaScript, вы бы использовали для написания сценариев в браузере?   -  person KaptajnKold    schedule 22.01.2011
comment
@KaptajnKold, я думаю, дело было в том, что одни и те же концепции ООП из других языков не должны быть напрямую перенесены в JS.   -  person David Tang    schedule 22.01.2011
comment
@KaptaijnKold: я бы использовал Javascript. Я бы не стал пытаться превратить его в какую-то ужасную мерзость!   -  person Lightness Races in Orbit    schedule 22.01.2011
comment
@Crowder: я не занимаюсь наследованием прототипов. Это вложенный/внутренний класс. В этом примере нет наследования. @Vidas - анон fn предназначен только для справки в моем модуле кода. Это ничего не добавляет к примеру. @Tomalak - похоже, у тебя проблемы с тем, что я делаю с моим кодом. Хм. Вы когда-нибудь думали о том, чтобы обратиться к терапевту по этому поводу?   -  person Cheeso    schedule 22.01.2011
comment
@LightnessRacesinOrbit, каждый день создается множество ужасных мерзостей, когда кто-то открывает почту Gmail, Yahoo и т. д.   -  person d-_-b    schedule 18.04.2012
comment
@LightnessRacesinOrbit такие мерзости? developer.mozilla.org/en-US/docs/Web/ JavaScript/   -  person Lorenzo Polidori    schedule 30.06.2013


Ответы (5)


Вероятно, это поможет вам, если вы избавитесь от таких понятий, как «тип», «класс» и т. д. при работе с javascript. В javascript нет различий между «типом», «классом», «функцией», «экземпляром» или «объектом» — это «объект» полностью.

Поскольку каждый «тип» является «объектом» и является изменяемым, вы не получаете ничего похожего на прирост эффективности строгой типизации, который вы могли бы получить от Java или C++, повторно используя одно определение типа. Думайте о «новом» операторе в javascript как о чем-то вроде «клонирования определения и вызова конструктора», после чего определение экземпляра все еще может быть изменено.

Итак, используйте свой первый рабочий пример: вы не получите никакой выгоды, если будете делать что-то другое.

person user85461    schedule 22.01.2011

Вот что я придумал после многих часов:

var Parent = function() {
  this.name = "Parent";

  this.Child = Child;
  this.Child.prototype.parent = this;
}

var Child = function() {

}

var parent = new Parent();
var child = new parent.Child();

console.log(child.parent.name);

Таким образом, вы можете создать столько родительских экземпляров, сколько хотите, с их дочерними элементами внизу, и каждый экземпляр дочернего элемента будет иметь доступ к своему родительскому экземпляру через переменную parent.

person htatche    schedule 17.02.2014

Единственный способ, который я могу придумать, - передать родительский объект дочернему конструктору:

MyType2.Child = function (parent, name) {
    this.parent = parent;
    this.name = name;
    this.Description = this.parent.name + "'s child, " + name;
};

И создайте его с помощью:

var x = new MyType2("Victor");

var c = new MyType2.Child(x, "Elizabeth");

Мое оправдание состоит в том, что конструктор Child имеет больше смысла в том, что конструктор Child является "статическим" свойством MyType2, а не экземпляром объекта x в вашем примере, поскольку здесь мы говорим о типах, которые являются одними и теми же во всех экземплярах MyType2.

person David Tang    schedule 22.01.2011

Я не проверял это, но вы должны иметь возможность использовать для этого JSON:

//code here
var number = {
    real : {
        value : 0, //default value
        rational : {
            integer : {
                isNegative : false,
                value : 0 //default value
            }
        }
    },
    imaginary : {
        //code
    }
};

var number2 = Object.create(number.rational.integer.prototype);

Теперь с этим может быть много проблем, функциональных или стилистических. Но это альтернатива другим подходам, размещенным здесь.

person Max Zlotskiy    schedule 26.08.2014

Это один из способов сделать это. Это объявляет внутренний объект, что своего рода подобно объявлению внутреннего класса и немедленному получению его экземпляра. Ссылка на внешний класс просто добавляется как атрибут объекта в объявлении.

// ES6 - example of an inner object.
class DancingFoo {
    constructor(radio) {
        this._radioListener = { 
            outer: this,
            onIsPlaying()      { this.outer._dancing = true; },
            onStoppedPlaying() { this.outer._dancing = false; }
        }
        radio.attachListener(this._radioListener);
    }
}

или если вам нужен внутренний класс, вы можете создавать экземпляры позже...

// ES6 inner class example    
class DancingFoo {
    constructor(radio) {
        let RadioListener = class { 
            constructor(outer) { this.outer = outer; }
            onIsPlaying()      { this.outer._dancing = true; }
            onStoppedPlaying() { this.outer._dancing = false; }
        }
        radio.attachListener(new RadioListener(this));
    }
}
person Todd    schedule 12.02.2020