从父构造函数

时间:2017-10-21 18:13:20

标签: typescript

假设以下的Typescript示例

class Animal {
    public name: string;

    constructor() {
        console.log(this.name);
    }
}

class Snake extends Animal {
    public name = 'BeatSSS';
}

let someSnake = new Snake();

当然,console.log()会记录undefined。预计BeatSSS

我们如何从具有子属性的父constructor()做事情?

实际想法:

  • super()调用child construct()解决问题,但我需要对每个孩子都这样做。不是很优雅的解决方案。
  • 在父构造上使用超时会记录正确的值,但是不可能立即使用类的实例(beacouse timeout尚未执行)。

4 个答案:

答案 0 :(得分:1)

如果你需要对每个孩子都这样做,你应该将name参数传递给父类的构造函数。

答案 1 :(得分:1)

你是用其他语言做的,在打字稿中做错了什么?

您正在设置正确运行的类所固有的属性。通过构造函数设置这些属性。

abstract class Animal {
    constructor(public name: string) {
    }
}

class Snake extends Animal {
    constructor(name: string = 'BeatSSS') {
        super(name);
    }
}

let someSnake = new Snake();         // use the default name 'BeatSSS'
let anotherSnake = new Snake('Foo'); // use a different name

class Dog extends Animal {
    constructor(name: string = 'Rover') {
        super(name);
    }
}

let someDog = new Dog();
let anotherDog = new Dog('Bar');

答案 2 :(得分:1)

我喜欢Jeff和Duncan已经提出过很多建议。但是,让我再展示一个选项。

abstract class Animal {
    constructor(public name: string) {
        console.log(this.name);
    }
}

class Snake extends Animal { }

let someSnake = new Snake("Snakeeeey!");

请注意这里的一些事情。

  • Animal类似乎确实是一个抽象的概念。您可以选择将其标记为abstract或将其定义为interface,但后者不会让您拥有任何实现(包括构造函数)。
  • TypeScript允许我们定义从构造函数初始化的字段。如果是公共财产,我们只需写constructor(public name: string)
  • Snake类没有显式定义构造函数。然而,在创建snake对象时,使用者必须提供name所需的Animal参数。

    在Jeff的代码中,Snake类具有name的默认值,该值由constructor(name: string = 'BeatSSS') { super(name); }实现。我不知道您的具体用例。如果蛇没有任何有意义的默认名称,我会避免完全声明这样的构造函数。

  • Duncan正确地解释说,调用super()本身并不是错误的。而且,这是必要的。

    当通过构造函数创建类型为Snake的对象时,父(Animal' s)构造函数必须先完成。因此,Animal构造函数无法"看到"在子(Snake' s)构造函数中发生的字段初始化的影响。由于构造函数调用的顺序,这是不可能的。

    因此,子类型传递父母构造函数信息的唯一选择是直接调用super(...)

  

super()呼吁孩子construct()解决问题,但我需要对每个孩子都这样做。不是很优雅的解决方案。

基本上没有完全定义子构造函数就是答案。如果您的子类必须包含构造函数,则会强制您将super()作为the very first instruction调用。

  

在父构造上使用超时会记录正确的值,但是不可能立即使用类的实例(beacouse timeout尚未执行)。

这是一个非常糟糕的选择。首先,通常不建议在构造函数中进行任何异步代码调用。

其次,如果您仍在setTimeout(() => this.fixMyState(), 0)构造函数中执行Animal,则会产生不良影响:对象构建后,对象处于错误状态。如果某些代码立即使用该对象怎么办? () => this.fixMyState(), 0甚至没有机会运行修复对象。在构造之后立即使对象处于良好状态是一个经验法则。否则,构造函数应该throw一个错误,以便没有代码可以尝试对处于错误状态的对象进行操作。

答案 3 :(得分:0)

在父级构造函数中获取子级值的 hacky 方法将是使用不具有setTimeout()属性的delay

使用您的示例,代码如下:

class Animal {
    public name: string;

    constructor() {
        setTimeout(() => console.log(this.name));
    }
}

class Snake extends Animal {
    public name = 'BeatSSS';
}

let someSnake = new Snake();

为什么起作用?

因为传递给setTimeout的函数将在清除当前调用堆栈后运行。此时,将完成对child属性值的分配,因此您可以访问父母的价值。

但是要小心!

在清除当前调用堆栈之前,该值将保持不确定状态。这意味着,例如,如果您使用该值在父对象中初始化该对象,则在调用堆栈结束之前将无法正确初始化该对象。

以下示例说明了此问题:

class Animal {
    public name: string;
    public name_copy: string;

    constructor() {
        setTimeout(() => {
            this.name_copy = name;
            console.log('assigning name_copy --->', this.name_copy);
        });
    }
}

class Snake extends Animal {
    public name = 'BeatSSS';
}

let someSnake = new Snake();
console.log('child name_copy --->', someSnake.name_copy);

此脚本将打印出:

child name_copy ---> **undefined**
assigning name_copy ---> **BeatSSS**