简体   繁体   English

如何在JavaScript中扩展闭包后面定义的类?

[英]How can I extend a class defined behind a closure in JavaScript?

I have a set of JavaScript "classes" where a base class defines functions that are then shared by an inherited class. 我有一组JavaScript“类”,其中基类定义函数,然后由继承的类共享。 It is working, and it is set up like this: 它工作正常,它设置如下:

var ThingA = function(name) {
    this.name = name;
};

ThingA.prototype = {
    sayHi: function() {
        alert('Hi, ' + this.name + '!');
    }
};


var ThingB = function() {
    ThingA.call(this, 'Charlie');
};

ThingB.prototype = new ThingA();
ThingB.prototype.constructor = ThingB;

var instanceOfB = new ThingB();
instanceOfB.sayHi();   // alerts 'Hi, Charlie!'

For reasons that are outside of my control, my company prefers to follow this pattern when writing JavaScript: 由于我无法控制的原因,我的公司在编写JavaScript时更喜欢遵循这种模式:

SomeClass = function() {

    // "Private" functions go here

    function somePrivateMethod() { 
        ...
    }

    return {

        // "Public" methods go here
        somePublicMethod: function() { ... }

    };
}();

Now, this is fine as far as things go, and it works well for many situations. 现在,就事情而言,这很好,并且在许多情况下它都能很好地运行。 But it is more of a functional style. 但它更像是一种功能性风格。 There is only one "class" instance, and everything is static. 只有一个“类”实例,一切都是静态的。

I've been asked to modify my working code to more closely match the style my company prefers. 我被要求修改我的工作代码,以更贴近我公司喜欢的风格。 So my question is, there a way to inherit from a class that is wrapped inside a factory class? 所以我的问题是,有没有办法从包含在工厂类中的类继承? It would look something like this: 它看起来像这样:

FactoryClassA = function() {

    var ThingA = function(name) {
        this.name = name;
    };

    ThingA.prototype = {
        sayHi: function() {
            alert('Hi, ' + this.name + '!');
        }
    };

    return {
         createThingA: function(name) {
             return new ThingA(name);
         }
    };
}();


FactoryClassB = function() {

    // Define a ThingB class that inherits from ThingA somehow

    return {
         createThingB: function() {
             return new ThingB();
         }
    };
}();


var instanceOfB = FactoryClassB.createThingB();
instanceOfB.sayHi();   // should alert 'Hi, Charlie!'

Is there a way to define ThingB wrapped in FactoryClassB that inherits from ThingA wrapped in FactoryClassA ? 有没有一种方法来定义ThingB包裹在FactoryClassB从继承ThingA包裹在FactoryClassA Thanks to this question , I know that I'm not going to be able to do it exactly like this. 感谢这个问题 ,我知道我不能完全像这样做。 I am thinking of using a method to extend a given class ... somehow? 我正在考虑使用一种方法来扩展给定的类......不知何故?

This answer seems close, but I'm having trouble figuring out the details of how to modify that example to fit with the specifics of my situation. 这个答案似乎很接近,但我无法弄清楚如何修改该示例的细节以适应我的具体情况。 I am willing to bend my company's usual pattern a little bit, but can I at least get closer to it? 我愿意稍微改变我公司的惯常模式,但我能否至少接近它?

UPDATE 1 更新1

In response to Adam's comment to just add a parameter to the factory class, here's where I'm stuck: 为了回应Adam的评论,只是在工厂类中添加一个参数,这就是我被困的地方:

ThingB.prototype = new ThingA();
ThingB.prototype.constructor = ThingB;

I can't figure out how to adapt these lines to make it work if I just pass in a parameter to the factory class method. 如果我只是将参数传递给工厂类方法,我无法弄清楚如何调整这些线以使其工作。

Below is what (I believe) you're looking for: 以下是(我相信)您正在寻找的内容:

FactoryClassA = function() {
    var ThingA = function(name) {
       this.name = name;
    };
    ThingA.prototype = {
        sayHi: function() {
            console.log('Hi, ' + this.name + '!');
        }
    };
    // Add the constructor back to the prototype
    // (see explanation below)
    ThingA.prototype.constructor = ThingA;

    return {
        createThingA: function(name) {
            return new ThingA(name);
        }
    };
}();

FactoryClassB = function() {
    // Bootstrapping:
    // Capture the instance, as we'll need it to set up the prototype
    var baseInstance = new FactoryClassA.createThingA();
    // Capture the constructor
    var baseConstructor = baseInstance.constructor;
    // Keep a reference to the base prototype
    var baseProto = baseConstructor.prototype;

    function ThingB(name) {
        // Call base constructor, along with our args
        baseConstructor.call(this, name);
    };
    ThingB.prototype = baseInstance;
    ThingB.prototype.constructor = ThingB;

    ThingB.prototype.sayHi = function() {
        console.log('here I am');
        // call the base class `sayHi`
        baseProto.sayHi.call(this);
    };

    return {
        createThingB: function(name) {
            return new ThingB(name);
        }
    };
}();

// Testing
var foo = FactoryClassB.createThingB("Indeed");
foo.sayHi();

// Output:
//   here I am 
//   hi indeed

Explanation : 说明

in FactoryClassA , this line is necessary: FactoryClassA ,这一行是必要的:

ThingA.prototype.constructor = ThingA;

Note that every prototype in JS is automatically created with a reference to its constructor. 请注意,JS中的每个原型都是通过对其构造函数的引用自动创建的。 For example, when you do: 例如,当你这样做时:

function T(){}

T.prototype already has a property called constructor which points back to T . T.prototype已经有一个名为constructor的属性,它指向T

However, in your implementation of ThingA , you reset the entire prototype, by doing ThingA.prototype = { ... } . 但是,在ThingA的实现中,通过执行ThingA.prototype = { ... }重置整个原型。 Therefore, you now have lost the reference to its constructor . 因此, 您现在已经丢失了对其构造函数的引用 In 99% of cases it is ok, and won't have any negative side effects (which is probably why most developers tend to forget it). 在99%的情况下它是可以的,并且不会产生任何负面影响(这可能是大多数开发人员倾向于忘记它的原因)。 However, in the case of inheritance, it may be necessary. 但是,在继承的情况下,可能是必要的。

Now, within FactoryClassB , we need to do some bootstrapping: 现在,在FactoryClassB ,我们需要做一些自举:

var baseInstance = new FactoryClassA.createThingA();
var baseConstructor = baseInstance.constructor;
var baseProto = baseConstructor.prototype;

Observe the last two lines, as they are pivotal to achieving inheritance in this design pattern. 观察最后两行,因为它们对于在此设计模式中实现继承至关重要。 First, since ThingA 's constructor is accessible via the prototype ( ThingA.prototype.constructor = ThingA ), then it means that given an instance of ThingA , we can directly retrieve its constructor. 首先,由于ThingA的构造函数可以通过原型( ThingA.prototype.constructor = ThingA )访问,这意味着给定一个ThingA实例,我们可以直接检索它的构造函数。 Since the constructor is the function itself, and since every function has a reference to its prototype, we can keep a reference of ThingA.prototype with baseConstructor.prototype . 由于构造函数本身就是函数,并且由于每个函数都有对其原型的引用,因此我们可以使用baseConstructor.prototype保留ThingA.prototype的引用。

Next is the critical part, where we set up the inheritance chain: 接下来是关键部分,我们在其中设置继承链:

function ThingB(name) {
    // Call the base constructor
    baseConstructor.call(this, name);
};
ThingB.prototype = baseInstance;
ThingB.prototype.constructor = ThingB;

The last line above is quite important, as it tells the prototype what its constructor is, otherwise it would still point to ThingA . 上面的最后一行非常重要,因为它告诉原型它的构造函数是什么,否则它仍然会指向ThingA

There you have it - prototypal inheritance. 你有它 - 原型继承。

Side note : 旁注

You can probably see how the above can get quite tedious, a little grotesque, and repetitive. 您可能会看到上述内容如何变得相当繁琐,有点怪诞和重复。 Ergo, you might want to consider an inheritance library like Fiber.js which follows the encapsulation pattern you desired (along with some bonuses like mixins and decorators). 你可能想要考虑像Fiber.js这样的继承库,它遵循你想要的封装模式(以及一些奖金,如mixins和decorators)。 Disclaimer: I authored the library . 免责声明:我撰写了图书馆

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM