打字稿,静态方法继承

Typescript, static method inheritance

我正在使用打字稿,我在 类

之间的静态继承有问题

任何人都可以向我解释以下结果:

class Foo {
    protected static bar: string[] = [];

    public static addBar(bar: string) {
        this.bar.push(bar);
    }

    public static logBar() {
        console.log(this.bar);
    }
}

class Son extends Foo {
    protected static bar: string[] = [];
}

class Daughter extends Foo {}

Foo.addBar('Hello');
Son.addBar('World');
Daughter.addBar('Both ?');
Foo.logBar();
Son.logBar();
Daughter.logBar();

当前结果:

[ 'Hello', 'Both ?' ]
[ 'World' ]
[ 'Hello', 'Both ?' ]

但我想要:

[ 'Hello' ]
[ 'World' ]
[ 'Both ?' ]

我有没有重新声明静态 bar 属性 的解决方案?

谢谢!

了解staticclass的关键是子类的构造函数继承了超类的构造函数。字面上地。 class只是在构造函数创建的实例之间设置继承,构造函数本身也在一个继承结构中。

FooSonDaughter的原型。也就是说Daughter.barFoo.bar,是继承的属性。但是你给了Son它自己的bar属性,用它自己的数组,所以在Son上查找bar找到 Son 自己的 bar,而不是 Foo 上的那个。这是发生这种情况的一个更简单的例子:

class Foo { }
class Son extends Foo { }
class Daughter extends Foo { }

Foo.bar = new Map([["a", "ayy"]]);
console.log(Foo.bar.get("a"));          // "ayy"

// `Son` inherits `bar` from `Foo`:
console.log(Son.bar === Foo.bar);       // true, same Map object
console.log(Son.bar.get("a"));          // "ayy"

// So does `Daughter` -- for now
console.log(Daughter.bar === Foo.bar);  // true, same Map object
console.log(Daughter.bar.get("a"));   // "ayy"

// Retroactively giving `Son` its own static `bar`
Son.bar = new Map();
console.log(Son.bar === Foo.bar);       // false, different Map objects
console.log(Son.bar.get("a"));          // undefined

这就是为什么在查看 Foo.barDaughter.bar 时会看到 ["Hello", "Both ?"] 的原因:它们是相同的 bar,指向相同的数组。但是您只能在 Son.bar 上看到 ["World"],因为它是指向不同数组的不同 bar

要将它们分开,您可能希望为每个构造函数提供其自己的 bar,尽管您 可以 使用 Map 执行 的操作].


关于事物组织方式的更多细节。有点像这样:

const Foo = {};
Foo.bar = [];
const Son = Object.create(Foo);
Son.bar = []; // Overriding Foo's bar
const Daughter = Object.create(Foo);
Foo.bar.push("Hello");
Son.bar.push("World");
Daughter.bar.push("Both ?");
console.log(Foo.bar);
console.log(Son.bar);
console.log(Daughter.bar);

如果你是新鲜的,这是一件非常令人惊讶的事情,但是你的三个 类 在记忆中看起来是这样的:

                                             +−−>Function.prototype 
                          +−−−−−−−−−−−−−−−+  |
Foo−−−−−−−−−−−−−−−−−−+−+−>|   (function)  |  |       
                    / /   +−−−−−−−−−−−−−−−+  |       
                    | |   | [[Prototype]] |−−+       +−−−−−−−−−−−+
                    | |   | bar           |−−−−−−−−−>|  (array)  |
                    | |   | addBar, etc.  |          +−−−−−−−−−−−+
                    | |   +−−−−−−−−−−−−−−−+          | length: 2 |
                    | |                              | 0: Hello  |
                    | +−−−−−−−−−−−−−+                | 1: Both ? |
                    |               |                +−−−−−−−−−−−+
                    +−−−−−−−−−−−−+  |
                                 |  |
              +−−−−−−−−−−−−−−−+  |  |
              |   (function)  |  |  |    
              +−−−−−−−−−−−−−−−+  |  |    
Daughter−−−−−>| [[Prototype]] |−−+  |
              +−−−−−−−−−−−−−−−+     |
                                    |
              +−−−−−−−−−−−−−−−+     |
              |   (function)  |     |       
              +−−−−−−−−−−−−−−−+     |       
Son−−−−−−−−−−>| [[Prototype]] |−−−−−+    +−−−−−−−−−−−+
              | bar           |−−−−−−−−−>|  (array)  |
              +−−−−−−−−−−−−−−−+          +−−−−−−−−−−−+
                                         | length: 1 |
                                         | 0: World  |
                                         +−−−−−−−−−−−+

@T.J.Crowder 在 中找到了对 OP 代码行为的非常详细的解释。

为避免需要重新定义静态成员,您可以采用这种方法:

class Foo {
    private static bar = new Map<string, string[]>();

    public static addBar(bar: string) {
        let list: string[];

        if (this.bar.has(this.name)) {
            list = this.bar.get(this.name);
        } else {
            list = [];
            this.bar.set(this.name, list);
        }

        list.push(bar);
    }

    public static logBar() {
        console.log(this.bar.get(this.name));
    }
}

class Son extends Foo {}

class Daughter extends Foo {}

Foo.addBar('Hello');
Son.addBar('World');
Daughter.addBar('Both ?');

(code in playground)