假设以下的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()
做事情?
实际想法:
答案 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
,但后者不会让您拥有任何实现(包括构造函数)。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**