Javascript继承依赖于构造函数参数

时间:2015-04-22 13:37:19

标签: javascript angularjs inheritance prototype

我想在Angular中实现原型继承,其中base类型被定义为Angular值。问题是设置我的孩子类型原型。假设这个简化的例子:

档案1

angular.module("Test")
       .value("BaseController", BaseController);

BaseController.$inject = [...];

function BaseController(...) {
    ...
}

BaseController.prototype.func = function () {
    ...
};

文件2

angular.module("Test")
       .controller("ChildController", ChildController);

ChildController.$inject = ["BaseController", ...];

function ChildController(BaseController, ...) {
    BaseController.call(this, ...);

    // save reference for later
    ChildController.base = BaseController;
    ...
}

// ERROR - Clearly why
ChildController.prototype = Object.create(BaseController.prototype);

// ERROR - instance properties aren't available on this level
ChildController.prototype = Object.create(ChildController.base.prototype);

继承

问题是在构造函数被实例化之前正在生成原型。但是在构造函数被实例化之前,我没有可能引用角度注入的BaseController

我能看到解决这个问题的唯一方法就是让我的BaseController公开定义,这样我甚至可以在角度注入构造函数之前访问它。我不喜欢这个,因为我不能在函数闭包中使用我的代码 private ,我也希望尽可能多地使用angular的功能,而不需要使用常见的Javascript来对抗Angular代码。< / p>

主要问题

有没有什么方法可以通过将基类型定义为角度值(或其他)来使原型继承工作?

2 个答案:

答案 0 :(得分:1)

此解决方案专门针对您的方法。您可以使用模块的run块来分配原型。在文件2中添加以下内容:

angular.module("Test").run(function(BaseController) {
  ChildController.prototype = Object.create(BaseController.prototype);
});

BaseController被注入并可用于创建原型。由于此代码在任何控制器实例化之前运行,因此您将获得原型继承。

另请注意,ChildController.$inject必须包含所有BaseController.$inject

另一种方法是将BaseController附加到模块本身:

angular.module("Test").BaseController = BaseController;
...
ChildController.prototype = Object.create(angular.module("Test").BaseController.prototype);

代码仍然是私有的,构造函数仍然只能通过模块访问。

您还可以寻找继承的替代方案。根据具体情况,分层控制器可能是一个可行的解决方案。

<div ng-controller="BaseController"><%-- Handle Generic stuff --%>
  ...
  <div ng-controller="ChildController"><%-- Handle specific stuff --%>

答案 1 :(得分:0)

  

重要信息

     
      
  1. 此解决方案专门针对与Angular相关的代码实现,该代码使用实际的Javascript原型来定义控制器/服务,而不仅仅是匿名函数,如Web上的日常示例所示 - 这意味着遵循{{3因为他没有使用真正的原型

  2. 而进一步扩展它   
  3. 所有构造函数必须使用$inject静态属性使用显式DI注释。

    可以解决这个限制      
        
    • 使用Angular injector annotate(如果内联提供注释 - 数组)或
    •   
    • 更改.inherits签名,以正确的顺序包含所有基本类型的构造函数参数作为其自己的参数,即
        Child.inherits(Base, baseInjection1, baseInjection2, ...)
    •   
  4.   

在Angular

中设置正确的原型继承

我已经提出了一种相当简单且最常用的方法来设置我的Angular控制器的类型继承。那么这种继承甚至可以超越它,并且可以与任何使用类型构造函数(控制器,服务等)的Angular资产一起使用。

生成的解决方案会更改原始文件&#39;内容以这种极其简单的形式出现:

File1中

angular.module("Test")
       .value("BaseController", BaseController);

BaseController.$inject = [...];

function BaseController(...) {
    ...
}

BaseController.prototype.func = function () {
    ...
};

文件2

angular.module("Test")
       .controller("ChildController", ChildController);

ChildController.$inject = ["BaseController", ...];

function ChildController(BaseController, ...) {
    // ALL MAGIC IS HERE!
    ChildController.inherits(BaseController, arguments);
    ...
}

所以我们要做的就是在我们的子类型构造函数中调用一个并提供基类型(由Angular DI注入到构造函数中)和子类型的构造函数参数(因此继承可以在运行基类型的构造函数时使用它们。)

实施.inherit功能

为了使事情变得通用我已将此函数添加到Function.prototype对象,因此它可用于所有函数(或更好的构造函数)。这就是它的实施方式:

Function.prototype.inherits = function inherits(BaseType, constructorInjections) {
    /// <summary>Sets type's inheritance to base type.</summary>
    /// <param name="BaseType" type="Function" optional="false">Base type to set for this child type.</param>
    /// <param name="constructorInjections" type="Array" optional="true">Child type constructor injection instances.</param>

    // return if both angular types don't use explicit DI
    if (!this.$inject || !BaseType.$inject) return;

    // DRY
    if (this.prototype.__proto__ === BaseType.prototype || Object.getPrototypeOf(this.prototype) === BaseType.prototype) return;

    // #region construct base object instance

    // make a quick-search dictionary of child constructor injections
    for (var i = 0, l = this.$inject.length, dic = {}; i < l; i++)
    {
        dic[this.$inject[i]] = i;
    }

    // create base type's constructor injections array
    for (var i = 0, l = BaseType.$inject.length, baseParams = []; i < l; i++)
    {
        baseParams.push(constructorInjections[dic[BaseType.$inject[i]]]);
    }

    // get base type's constructed instance
    var baseInstance = BaseType.apply(baseInstance = {}, baseParams) || baseInstance;

    // #endregion

    // #region set type inheritance chain
    if (Object.setPrototypeOf)
    {
        Object.setPrototypeOf(this.prototype, BaseType.prototype);
    }
    else
    {
        // doesn't do the same thing, but it should work just as well
        angular.extend(this.prototype, BaseType.prototype, { __proto__: BaseType.prototype });
    }
    // #endregion

    // #region add base class generated instance to prototype
    for (var i = 0, keys = Object.keys(baseInstance), l = keys.length; i < l; i++)
    {
        this.prototype[keys[i]] = baseInstance[keys[i]];
    }
    // #endregion
};