【问题标题】:Typescript, static method inheritance打字稿,静态方法继承
【发布时间】:2017-09-11 01:02:20
【问题描述】:

我正在使用 typescript,但类之间的静态继承存在问题

谁能解释一下下面的结果:

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 属性的解决方案?

谢谢!

【问题讨论】:

  • 去掉static
  • 删除static 会使属性绑定到实例而不是类型。 OP 希望属性绑定到类型。

标签: javascript typescript ecmascript-6 typescript2.0


【解决方案1】:

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

FooSonDaughter 的原型。这意味着Daughter.bar Foo.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 执行Nitzan Tomer suggests 的操作。


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

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−−−−−−−−−−−−−−−−−−−+−+−>| (函数) | | / / +−−−−−−−−−−−−−−−−+ | | | | [[原型]] |−−+ +−−−−−−−−−−−−+ | | |条形 |−−−−−−−−−−>| (数组) | | | | addBar 等 | +−−−−−−−−−−−−−+ | | +−−−−−−−−−−−−−−−−+ |长度:2 | | | | 0:你好| | +−−−−−−−−−−−−−−+ | 1:两者都有? | | | +−−−−−−−−−−−−−+ +−−−−−−−−−−−−−+ | | | +−−−−−−−−−−−−−−−−+ | | | (函数) | | | +−−−−−−−−−−−−−−−−+ | | 女儿-------->| [[原型]] |−−+ | +−−−−−−−−−−−−−−−−+ | | +−−−−−−−−−−−−−−−−+ | | (函数) | | +−−−−−−−−−−−−−−−−+ | 儿子----------->| [[原型]] |−−−−−−+ +−−−−−−−−−−−−+ |条形 |−−−−−−−−−−>| (数组) | +−−−−−−−−−−−−−−−−+ +−−−−−−−−−−−−+ |长度:1 | | 0:世界 | +−−−−−−−−−−−−−+

【讨论】:

  • 您说“与 Foo 的链接已损坏”,但我认为技术术语是“阴影”,对吧?也就是说,Foo.barSon.bar 是两个独立的对象,Foo.bar 仍然在 Son.bar 的原型链中,但是子对象 遮蔽 父对象,方式相同内部作用域可以隐藏外部作用域中的变量。 (我说“我认为”是因为我不是专家,但这是我理解事物的方式,它对我帮助很大。)
  • @Coderer - 我不认为我听说过与对象属性(只是嵌套范围内的变量)一起使用的术语 shadowed,但它在概念上是相似的,是的。因为Son 有自己的bar,所以在Son 上查找bar 会在Son 上找到它时停止,并且不会沿着原型链继续;这与嵌套标识符解析非常相似。所以我没有听说它是这样使用的,但是......我认为它确实有效。 :-) Re "...Foo.barSon.bar 是两个独立的对象,Foo.bar 仍在 Son.bar 的原型链中..." 我不认为你应该有那些.bars,对吧?
  • 是的,我编辑太晚了,但我的意思是阅读“Foo.barSon 的原型链中”,因为Son 可以访问Son.barFoo.bar,如果您处于可以使用 super 绕过您自己的原型并进一步向上查找的环境中。
【解决方案2】:

在@T.J.Crowder 的answer in this thread 中可以找到关于 OPs 代码行为的非常详细的解释。

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

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)

【讨论】:

  • 请记住 Map 可以有非字符串键,所以你可以只通过构造函数本身键... :-)
  • @T.J.Crowder 是的。但我总是更喜欢使用“字符串 id”,除非我真的需要实例(在这种情况下是构造函数)来做其他事情。这可能是我从其他语言中继承来的偏好。
  • @NitzanTomer 确保您将压缩程序配置为不破坏函数名称。我们对此感到厌烦。您确实应该使用构造函数引用,因为来自不同模块的两个类可能具有相同的名称。 uglifyjs.net/#toggle_text_mangle_fnames
猜你喜欢
  • 2019-01-24
  • 1970-01-01
  • 2017-12-04
  • 2012-06-21
  • 2015-03-20
  • 1970-01-01
  • 2013-10-20
  • 2017-07-14
相关资源
最近更新 更多