Javascript,内部类以及如何有效地访问父作用域

时间:2011-01-22 15:10:27

标签: javascript oop closures

在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 var超出了范围。

Child实例(在构造函数中,或在任何类函数中)是否有一种有效的方式来访问父(MyType2)实例?


我知道我可以将Child类定义为一个独立的非嵌套类,然后在ctor中,只需传递Parent实例。但这会创建对父实例的N个引用,每个子实例都有一个引用。这似乎是我想避免的低效率。

感谢任何提示。


编辑 - 我希望Child能够访问父实例的原因是,父拥有一个创建起来相当昂贵的对象 - 类似于数据库连接 - 而且我是就像孩子能够利用那个东西一样。

5 个答案:

答案 0 :(得分:3)

如果你在处理javascript时放弃了“类型”,“类”等概念,它可能会帮助你。在javascript中,没有区别于“类型”,“类”,“函数”,“实例”或“对象” - 它一直是“对象”。

由于每个“类型”都是“对象”并且是可变的,因此通过重用单个类型定义,您无法获得从Java或C ++中获得的强类型效率增益。将javascript中的“new”运算符想象为“克隆定义并调用构造函数”,之后仍然可以更改实例的定义。

所以请按照你的第一个工作示例:做一些与众不同的事情,你不会获得任何好处。

答案 1 :(得分:2)

这是我在几个小时后出现的:

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

通过这种方式,您可以使用Childs实例化所需数量的父母 在下面,每个Child实例都可以访问它的父实例 通过变量

答案 2 :(得分:1)

我能想到的唯一方法是将父对象传递给Child构造函数:

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构造函数是MyType2的“静态”属性更有意义,而不是示例中的实例化对象x,因为我们正在谈论关于类型,在MyType2的所有实例中都是相同的。

答案 3 :(得分:0)

我没有对此进行测试,但您应该可以使用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);

现在可能存在许多问题,功能性或风格性。但它可以替代其他方法。

答案 4 :(得分:0)

这是一种实现方法。这声明了一个内部对象-这是 kinda ,就像声明一个内部类并立即获取它的实例一样。只需在声明中将对外部类的引用作为对象属性添加即可。

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