Person扩展Object但不覆盖toString

时间:2018-04-10 14:18:47

标签: typescript

我希望在扩展Object的类上定义自定义toString会覆盖基类的toString函数。但是,这似乎不适用..有没有人知道为什么会这样?

代码如下:

class Person extends Object {
    constructor(public name:string){
        super(); 
        this.name = name;
    }

    public toString():string {
        return 'hi i am ' + this.name;
    }
}

var john = new Person("john");
console.log(john.toString());

这段代码输出[object object],我希望它能调用person类的toString ..

当我像下面的代码一样编写它时,它可以工作,但理由是什么?

class Person {
    constructor(public name:string){
        this.name = name;
    }

    public toString():string {
        return 'hi i am ' + this.name;
    }
}

var john = new Person("john");
console.log(john.toString());

进一步测试时,当我创建一个从Object扩展的类时,它的所有方法都不可用......所以如果我要创建一个类如下的类:

class Person extends Object {
    constructor(public name:string){
        super(); 
        this.name = name;
    }

    public toStringTest():string {
        return 'hi i am ' + this.name;
    }
}

var john = new Person("john");
console.log(john.toStringTest());   <-- ERROR HERE AT RUNTIME

在运行时会出错,说明此方法不可用..

这是预期的行为吗?

对于记录:我在NodeJS中的VSCode中使用TypeScript并从TS转换回es5 ...然后我使用NodeJS运行它..

TIA,

约翰。

1 个答案:

答案 0 :(得分:2)

嗯,原因在于转换后的代码:

var __extends = (this && this.__extends) || (function () {
    var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var Person = /** @class */ (function (_super) {
    __extends(Person, _super);
    function Person() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    Person.prototype.toString = function () {
        return 'hi';
    };
    return Person;
}(Object));
var p = new Person();

正如您在Person函数(Person的构造函数)的定义中所看到的那样,它将结果返回给调用_super.apply(this, arguments)。所以,实质上是返回Object(),它基本上是一个空对象。而且,此外,它将结果对象的原型设置为Object的原型,实际上是Object的直接后代并忽略Person原型。实际上,如果您在控制台中执行代码并执行:

p instanceof Person

你得到false

如果你这样做

p.__proto__ === Object.prototype

你得到true

所以永远不要来自Object。隐含地,所有目标都已经存在。

但是,为什么会发生这种情况?

好吧,正如我们所看到的,构造函数返回_super.apply(this, arguments) || this

这意味着,如果调用没有new的构造函数返回undefined(通常用于几乎所有类,其构造函数没有return语句),那么我们得到{{ 1}},并且所有工作都按预期进行。

问题是,根据ECMASCript规范,如果对它的调用(调用this)返回不是对象的东西,则构造函数返回this。对于返回空对象的.call()而言,情况并非如此,而大多数构造函数都没有(如上所述,它们返回Object())。所以这里,TypeScript遵循相同的约定,并且,undefined本身返回Object(),所有派生类也是如此,只有增强属性。

在示例中,如果您向{ }添加name属性,则可以执行以下操作:

Person

即使const p = new Person(); p.name = 'John'; 不是p类型,而是Person

TypeScript转换使构造函数始终返回Object。通常,这与_this相同(当您使用this调用时,构造函数隐式返回的内容),因此不会造成任何损害。但是,当然,这意味着,如果您在控制台中执行转换后的代码,则可以执行以下操作:

new

并且它会给你一个漂亮的空对象(就像Person(); 一样,从中明确地显示它!这样,行为是合乎逻辑的。)

如果您添加属性,例如Object,则调用name将为您提供具有此类属性的对象。

所以,正如更正确的结论:不要从没有Person()的情况下工作的构造函数派生出来,并且当你以这种方式调用时,你不确定他们做了什么。