TypeScript中的高级JavaScript继承

时间:2018-10-01 17:32:50

标签: javascript typescript inheritance design-patterns solid-principles

TypeScript中的高级JavaScript继承

JavaScript的奇妙方面之一是对象可以从其他对象继承封装的多种不同方式。但是,从这个角度来看,TypeScript对编写模块的可能性施加了严格的限制。

在JavaScript中,您可以选择通过使用 Constructor Hijacking (该语言的一种极为强大的功能)来实现多重继承-而是Mixin功能。

var Base = function Base() {

    function f() {
        console.log('datum: %s', this.datum);
    }

    function method() {
        this.a();
        this.b();
        this.c();
    }

    // export precepts
    this.datum = true;
    this.f = f;
    this.method = method;

    return this;
};

var A = function A() {

    function a() {
        this.f();
    }

    // export precepts
    this.a = a;

    return this;
};
var B = function B() {

    function b() {
        this.f();
    }

    // export precepts
    this.b = b;

    return this;
};
var C = function C() {

    function c() {
        this.f();
    }

    // export precepts
    this.c = c;

    return this;
};

var Klass = function Klass() {
    var config = { };

    function init() {
        this.method();
    }

    // export precepts
    Base.call(this);
    A.call(this);
    B.call(this);
    C.call(this);
    this.config = config;
    this.init = init;

    return this;
};

var klass = new Klass();
klass.init();
// > datum: true
// > datum: true
// > datum: true

这使开发人员可以将代码分解为离散的模块,这些模块仅需遵循某种模式或约定即可扩展另一个模块,从而保持SOLID原始的“单一责任原则”和“开闭原则”。

分析

上面的代码应记录字符串datum: true 3次。这是因为Klass 修饰(或混入)Base类,以便类A-C在调用{{时不会抛出运行时错误。 1}}。该过程的CallStack如下所示:

  • [Klass]初始化
  • [Base]方法
  • [A] a
  • [Base] f
  • [控制台]日志
  • [B] b
  • [Base] f
  • [控制台]日志
  • [C] c
  • [Base] f
  • [控制台]日志

注释

CallStack是相当随意和琐碎的。另外,该代码可以看作是非SOLID代码,但是只要假设我们正在使用模板方法模式,就可以避免示例的繁琐。

此外,毫无疑问,将有一个灵魂要鼓起勇气,谈论我们对上述示例的所有了解如何违反TypeScript。请记住,“ TypeScript是JavaScript的超集”是简单,明显和公然的错误-我什至不会争论为什么,如果您正在使用TypeScript,您应该已经知道这一点。

问题:

鉴于上面的代码,如何使用有效的TypeScript语法实现这种功能?我仍然愿意在必要时利用“设计模式”,尽管这仍然不理想。

1 个答案:

答案 0 :(得分:0)

Mixins为您提供所需的大部分东西。您的示例为:

class Base { 
    datum = true;
    // We can't make these methods abstract because TypeScript currently
    // doesn't support tracking whether mixins implement abstract methods. 
    a() {
        throw new Error("not implemented");
    }
    b() {
        throw new Error("not implemented");
    }
    c() {
        throw new Error("not implemented");
    }
    f() {
        console.log('datum: %s', this.datum);
    }
    method() { 
        this.a();
        this.b();
        this.c();
    }
}

function mixA<Orig extends {new(...args: any[]): Base}>(base: Orig) { 
    return class extends base {
        a() {
            this.f();
        }
    };
}
function mixB<Orig extends {new(...args: any[]): Base}>(base: Orig) { 
    return class extends base {
        b() {
            this.f();
        }
    };
}
function mixC<Orig extends {new(...args: any[]): Base}>(base: Orig) { 
    return class extends base {
        c() {
            this.f();
        }
    };
}

class Klass extends mixA(mixB(mixC(Base))) { 
    config = {};
    init() { 
        this.method();
    }
}

var klass = new Klass();
klass.init();